"""
A RepoHelper can checkout repositories,
return the pathname where a repository is stored,
and return the usernames from a course.

Authors:  David Mutchler, David Lam, Mark Hays and their colleagues.
Version 1.1:  May, 2015.
"""
# FIXME: Above comment is no longer correct.

import os
import main_for_testing
import enum


# NOTE: Change the GRADING_FOLDER as needed for your computer.
# 'C:/EclipseWorkspaces/csse120-grading/'
GRADING_FOLDER = '/Users/david/EclipseWorkspaces/grading/'
URL_FOR_SVN_REPOS = 'http://svn.csse.rose-hulman.edu/repos/'

STUDENTS_TO_TEST_FOLDER = 'students_to_test'

USERNAME = 'mutchler'


class CheckoutResult(enum.Enum):
    # TODO: Use enum.auto() when I move to 3.6 (it is introduced there)
    SUCCESS = 1
    FAILURE = 2
    SKIP = 3

# TODO: Move the SVN-specific stuff from the following class
#       to SVNHelper class.  Leave RepoHelper an abstract class.


class RepoHelper(object):
    """
    Abstract class, subclass must specify ???
    A RepoHelper can checkout repositories for a course,
    return the pathname where a repository is stored,
    and return the usernames from a course.
    """
    # TODO: Fix the above comment.

    def __init__(self, what_to_grade, who_to_grade,
                 ask_for_password=True):
        self.what_to_grade = what_to_grade
        self.who_to_grade = who_to_grade
        if ask_for_password:
            self.password = input('Enter your SVN password: ')

    def checkout_for_grading(self, students=None,
                             replace_existing_repos=False):
        """
        Checkouts the given project for all the given list of students.
        Returns three lists:
          -- students for which the checkout appears to have failed
          -- students skipped (because they were already checked out)
          -- all remaining students
        """
        if students is None:
            students = self.who_to_grade.students

        # CONSIDER: Allow checking out things other than projects??

        failures, skips, successes = [], [], []
        result_list = {CheckoutResult.FAILURE: failures,
                       CheckoutResult.SUCCESS: successes,
                       CheckoutResult.SKIP: skips}

        for student in students:
            url = self.get_url(student)
            folder = self.get_grading_folder(student)

            self.start_checkout(student)

            if not replace_existing_repos and os.access(folder, os.F_OK):
                checkout_result = CheckoutResult.SKIP
            else:
                checkout_result = self.checkout(url, folder)

            self.end_checkout(student, checkout_result)

            result_list[checkout_result].append(student)

        return (failures, skips, successes)

    def get_url(self, student):
        """
        Returns the URL for the given student's WhatToGrade.
          :type student: str
        """
        repo = (URL_FOR_SVN_REPOS
                + self.what_to_grade.course.students_repo
                + '-' + student + '/'
                + self. what_to_grade.project_name)
        return repo

    def get_grading_folder(self, student=None):
        """
        Returns the folder into which to checkout the project
        for the given student (or just the enclosing folder
        for all the students if student is None). 
          :type student: str
        """
        # FIXME:
        # This presumes a folder structure that others might not want.
        # But at least the structure is encapsulated in this function.
        folder = (GRADING_FOLDER
                  + self.what_to_grade.course.term + '/'
                  + self.what_to_grade.project_name + '/')

        if student:
            folder += STUDENTS_TO_TEST_FOLDER + '/' + student + '/'

        return folder

    def start_checkout(self, student):
        print('Checking out: {}'.format(student))

    def end_checkout(self, student, result):  # Subclass can override
        pass

    def checkout(self, url, folder):
        """
        Checks out the given URL into the given folder.
        Returns Checkout.SUCCESS or Checkout.FAILURE.
        """
        # FIXME: You need to make Tortoise learn your password somehow.
        #    Otherwise, use something like:
        #      os.system('svn checkout ' + url + ' ' + folder
        #            + ' --username mutchler --password XXX')
        #    But note that the latter is a security hazard
        #    (see me for details).
        print(url, folder)
#         try:
        # If the system knows your SVN credentials ...
        #             command = 'svn checkout ' + url + ' ' + folder
        #             result = subprocess.run(command)
        command = ('svn checkout --username ' + USERNAME
                   + ' --password ' + self.password + ' '
                   + url + ' ' + folder)
        result = os.system(command)
        return (CheckoutResult.SUCCESS if result == 0
                else CheckoutResult.FAILURE)
#         except:
#             print('failed')
#             return CheckoutResult.FAILURE


# ----------------------------------------------------------------------
# NOTE: The following code assumes that you have installed Tortoise SVN
#       and put command-line tools (e.g.   svn   ) in your system path.
# ----------------------------------------------------------------------
class SVNRepoHelper(RepoHelper):
    """
    An Subversion (SVN) implementation, hosted by CSSE,
    of the RepoHelper abstract class.
    """
    # TODO: Pull SVN-specific stuff from RepoHelper (above) to here.


class StandardRepoHelper(SVNRepoHelper):
    """ The default class to use as RepoHelper is a SVNRepoHelper. """


def main():
    main_for_testing.main()

if __name__ == '__main__':
    main()


# ALL THIS BELONGS IN WhoToGrade class:
#     def get_usernames(self, groups=None):
#         """
#         The argument, if present, is either a section number
#         or a sequence of group names.  Returns a list of usernames
#         for that section or for that sequence of group names.
#         """
#         if not groups:
#             sections = self.course.sections
#         else:
#             # TODO: Is there a better way to tell whether
#             #       groups is a sequence?
#             try:
#                 len(groups)
#                 # FIXME: Deal with this case.  See commented-out code.
#             except:
#                 sections = [groups]
#
#         # FIXME: Really should get this information from the
#         #        course web site.  See commented-out code.
#         usernames = []
#         for section in sections:
#             file = open('usernames-' + str(section) + '.txt', 'r')
#             for username in file.readlines():
#                 usernames.append(username.strip())
#         return usernames

#     url_prefix = roster_folder + COURSE + '-' + TERM + '-'
#     for group in groups:
#         url = url_prefix + group + '.txt'
#         print(url)
#         usernames_as_byte_array = urllib.request.urlopen(url).read()
#         usernames_in_group = usernames_as_byte_array.decode().split()
#         usernames.extend(usernames_in_group)
#
#     return usernames

# The following is no longer relevant, it seems:
#     def fix_all_for_eclipse(self, project, students):
#         """
#         For each of the given students, renames the project to be the
#         student's username.  This allows all the projects to exist in a
#         single Eclipse workspace.
#         Returns a list of students for whom this failed for any reason.
#         """
#         failures = []
#         for student in students:
#             try:
#                 folder = self.get_grading_folder(student, project)
#                 result = self.fix_for_eclipse(folder, project, student)
#                 if not result:
#                     failures.append(student)
#             except:
#                 failures.append(student)
#
#         return failures
#
#     def fix_for_eclipse(self, folder, project, student):
#         """
#         Modifies the  .project  file in the given folder to rename
#         the project from its name to the given student (username).
#         This allows Eclipse to import  multiple students' projects
#         for the same assignment (all of which share the same project
#         name before running this function).
#         Returns  True  if this action succeeded, else returns  False.
#         """
#         # FIXME: The following is brittle and WILL BREAK if
#         #        Eclipse changes its project filenames.
#         filename = folder + '/.project'
#         try:
#             with open(filename, 'r') as file:
#                 file_contents = file.read()
#
#             lines = file_contents.split('\n')
#             with open(filename, 'w') as file:
#                 for line in lines:
#                     print(re.sub(project, student, line), file=file)
#         except:
#             # FIXME: Figure out whether this detects failures correctly.
#             print('Could not open the file:')
#             print('   ', filename)
#             ('Fix and try again.\n')
#             return False
#
#         return True


#     csse120 = course.Course('csse', '120', '201640', ['01'])
#     what_to_grade = grader.ThingToGrade(csse120, 'Session10_MoreImplementingClasses')
#     rh = RepoHelper(what_to_grade)
#     a, b, c = rh.checkout_for_grading(what_to_grade, csse120.get_usernames())
#     print(a)
#     print(b)
#     print(c)


# ----------------------------------------------------------------------
# NOTE: The following code is not currently used.
# ----------------------------------------------------------------------
#
#
# class Student(object):
#     def __init__(self, username, name, campus_mail, major, class_, year,
#                  advisor, email):
#         self.username = username
#
#
# # class Checker_Outer(object):
# #     default_course = 'csse120'
# #
# #     def __init__(self, course=Checker_Outer.default_course, term=None):
# #         self.course = course
# #         # TODO: Determine the term from today's date.
# #         self.term = 201510
# #         # TODO: Determine the sections from Schedule Lookup Page.
# #         self.sections = ['01', '02', '03', '04']
# #
# #         self.url_for_course_lookup = URL_FOR_SCHEDULE_LOOKUP
# #
# #     def enrolled_students(self, section=None):
# #         """
# #         Returns a list of all the Student  (u that are members of the given sequence
# #         of groups.  The usernames must be stored in text files that
# #         are in the appropriate place.
# #         """
# #         students = []
# #         url_prefix = roster_folder + COURSE + '-' + TERM + '-'
# #     for group in groups:
# #         url = url_prefix + group + '.txt'
# #         usernames_as_byte_array = urllib.request.urlopen(url).read()
# #         usernames_in_group = usernames_as_byte_array.decode().split()
# #         usernames.extend(usernames_in_group)
# #
# #     return usernames
#
#
# # def main():
# #     start(0)
# #
# #
# # def start(project_number):
# #     project = PROJECTS[project_number]
# #     students = get_usernames_from_rosters()
# #
# #     failures1 = checkout_all(project, students)
# #     failures2 = fix_all_for_eclipse(project, students)
# #     print('Failed checkout:', failures1)
# #     print('Failed fix-for-Eclipse:', failures2)
# #
# #     remove_extra(project, students)
#
#
# #     checkout_solution(project)
# #     folder = get_grading_folder(SOLUTION_USERNAME, project)
# #     fix_for_eclipse(folder, project + SOLUTION_SUFFIX,
# #                     SOLUTION_USERNAME)
#
#
#
#
#
#
#
# def checkout_solution(project_to_grade, solution_name=None):
#     """
#     Checkouts the solution for the given project.
#     Puts it in the same place as the student solutions are placed.
#     Returns True if succeeded, else False.
#     """
#
#     url = get_solution_url(project_to_grade)
#     folder = get_grading_folder(SOLUTION_USERNAME, project_to_grade)
#     if os.access(folder, os.F_OK):
#         print('WARNING: SKIPPING', folder, 'because it already exists')
#         return False
#     success_or_failure = checkout(url, folder)
#     return success_or_failure
#
#
#
# def get_solution_url(project_to_grade):
#     """ Returns the URL for the solution for the given project. """
#     repo = COURSE_REPO_NAME + '/' + ECLIPSE_PROJECTS + '/' + TERM
#     solution = project_to_grade + SOLUTION_SUFFIX
#     return URL_FOR_SVN_REPOS + repo + '/' + solution
#
#
#
#
#
#
#
#
# # Functions below are NOT correct.
#
# def remove_extra(project_to_grade, students):
#     """
#     Removes all unnecessary information from the projects:
#       -- All SVN information
#       -- All .docx and .pdf files
#     """
#     for student in students:
#         folder = get_grading_folder(student, project_to_grade)
#         remove_extra_in_folder(folder)
#
#
# def remove_extra_in_folder(folder):
#     """
#     At the TOP level of the given folder,
#     as well as in the   src   subfolder (if it exists),
#     removes any of the following that exist:
#       *.docx, *.pdf, *.gif
#     """
#     # FIXME: THIS IS A DANGEROUS FUNCTION.
#     #        If the folder is somehow not what is intended,
#     #        LOTS of files can be IRREVOCABLY DELETED.
#
#     # Temporary attempt at safety:
#     try:
#         assert(folder.startswith(GRADING_FOLDER))
#     except:
#         print('ERROR ERROR ERROR in remove_extra_in_folder')
#         raise Exception('Error in remove_extra_in_folder')
#
#     # Here is the dangerous part:
#     for file in os.listdir(folder):
#         if (file == '.svn'):
#             pass  # shutil.rmtree(folder + '/.svn')
#         if fnmatch.fnmatch(file, '*.pdf'):
#             os.remove(folder + '/' + file)
#         if fnmatch.fnmatch(file, '*.docx'):
#             os.remove(folder + '/' + file)
#
#     for file in os.listdir(folder + '/src'):
#         if fnmatch.fnmatch(file, '*.gif'):
#             os.remove(folder + '/src/' + file)
#