import os, shutil
from coursebox.core.info_paths import get_paths
from coursebox.core.info import class_information
from snipper.snip_dir import snip_dir
from coursebox.material.homepage_lectures_exercises import fix_shared
from snipper.snipper_main import censor_file
from pathlib import Path
import fnmatch
import glob
from coursebox.material.documentation import fix_all_shared_files

def setup_student_files(run_files=True,
                        cut_files=True,
                        censor_files=True,
                        week=None, extra_dirs=None,
                        include_solutions=None,
                        projects=None,
                        setup_lectures=False,
                        strict=True,
                        fix_shared_files=True,
                        include_exam_examples=False, # Include the directory with exam-related examples.
                        verbose=True,
                        students_irlc_tools=None, # Destination dir for the irlc folder (the one we really want to deploy).
                        midterms=None, # Can be a list: [0, 1]
                        include_docs=False, # Include the documentation directory, i.e. 02465public/src/docs.
                        convert_svg=True, # Whether to convert SVG file when deploying. Leave to true, unless we are doing CI/CD in which case inkscape may not be installed.
                        # package="cp",
                        # fix_shared_files_func=None,
                        ):

    if midterms is None:
        midterms = []
    from coursebox.core import info_paths
    from coursebox.core.info import class_information
    # info = class_information()
    from coursebox.core.info import core_conf

    if len(core_conf['projects_all']) > 0:
        PACKAGE = list(core_conf['projects_all'].values()).pop()['module_public'].split(".")[0]
    else:
        PACKAGE = [w for w in core_conf['weeks_all'].values() if 'module_public' in w][0]['module_public'].split(".")[0]

    if censor_files:
        assert not run_files, "You cannot run files while you are censoring them -- your scripts will crash. Call with run_files=False."

    week = [week] if isinstance(week, int) else week
    if verbose:
        print("toolbox running. Fixing student files by copying to student dir")
    paths = get_paths()
    if include_solutions is None:
        include_solutions = []

    if projects is None:
        projects = []

    public_irlc_tools = f"{paths['02450public']}/src/{PACKAGE}"


    if students_irlc_tools is None:
        if censor_files:
            students_irlc_tools = f"{paths['02450students']}/{PACKAGE}"
        else:
            students_irlc_tools = f"{paths['02450students']}_complete/{PACKAGE}"

    if not os.path.isdir(students_irlc_tools):
        os.makedirs(students_irlc_tools)

    init_dest = students_irlc_tools+"/__init__.py"
    shutil.copy(public_irlc_tools + "/__init__.py", init_dest)
    for fpy in glob.glob(public_irlc_tools + "/*.py"):
        shutil.copy(fpy, students_irlc_tools + "/" + os.path.basename(fpy))

    if os.path.isfile(public_irlc_tools + "/../.coveragerc"):
        shutil.copy(public_irlc_tools + "/../.coveragerc", students_irlc_tools+ "/../.coveragerc")
    # assert False
    censor_file(init_dest)

    # Check for exclusion mask.
    exclude = list( info_paths.core_conf.get('student_files', {}).get('exclude', []) )

    if extra_dirs is None:
        extra_dirs = list(info_paths.core_conf.get('student_files', {}).get('extra_dirs', []))

    print("Extra dirs are", extra_dirs)
    exclude += [f'tests_week{w if w >= 10 else f"0{w}"}.py' for w in range(0,14) if w not in week]
    exclude = exclude + ["**/" + ex for ex in exclude]
    if not include_exam_examples:
        exclude += ['*/exam_tabular_examples/*']
    inclusion = [] # 'ex09old/cache']
    hws = []
    for m in midterms:
        # Copy the midterm from the exam directory into the public repository. This will ensure the midterm is later propagated to the student directory.
        # The midterm should always be distributed as a .zip file: This is the only legal distribution mechanism.
        # The midterm in the student directory does not need to be super-duper up to date. This is not the file which is distributed 'officially'.
        if verbose:
            print(m)

    if extra_dirs is None:
        # extra_dirs = []

        extra_dirs = ['utils', 'tests', 'exam/exam2023spring', 'pacman', 'gridworld', 'car'] # 'assignments',
        for m in midterms:
            if m == 0:
                extra_dirs += ['exam/midterm2023a']
            if m == 1:
                extra_dirs += ['exam/midterm2023b']

        # if include_exam_examples:
        #     extra_dirs += ['exam_tabular_examples']
    if setup_lectures:
        extra_dirs += [
            # 'lectures/chapter0pythonC',
            # 'lectures/chapter0pythonA',
            # 'lectures/chapter0pythonB',
            # 'lectures/chapter1',
            'lectures',
            # 'workshop'
        ]

    import coursebox.core.info

    for id in projects:
        # if setup_projects:
        if 'projects_all' in info_paths.core_conf:
            p = info_paths.core_conf['projects_all'][id]['class']
            extra_dirs += [os.path.basename(os.path.dirname(p.mfile()))]
        else:

            if id <= 3:
                extra_dirs += [f'project{id}'] # , 'project1', 'project2', 'project3']
            else:
                extra_dirs.append(f'project3i')

    if week is not None:
        for w in week:
            ex = str(w)
            ex = "ex" + ("0" + ex if len(ex) < 2 else ex)
            base = public_irlc_tools +"/"+ex
            if not os.path.isdir(base):
                continue
            d = {'base': base,
                 'out': students_irlc_tools +"/"+ex,
                 'exclusion': exclude,
                 'inclusion': inclusion,
                 'include_solutions': w in include_solutions,
                 'week': w}
            hws.append(d)

        weekn = 101
        for weekn, d in enumerate(extra_dirs):
            dutil = {'base': public_irlc_tools + "/" + d,
                 'out': students_irlc_tools + "/" + d,
                 'exclusion': exclude,
                 'inclusion': inclusion,
                'include_solutions': False,
                 'week': f'{weekn+100}'}
            hws.append(dutil)
        weekn += 1

    else:
        raise Exception("Specify a week")

    if len(hws) >  0:
        info = class_information(verbose=verbose)
    else:
        info = None
    for hw in hws:
        # if "ex08" in hw['out']:
        #     print("ex08")
        print("Fixing hw", hw['out'])
        n = fix_hw(paths=paths, info=info, hw=hw, out= hw['out'], output_dir=paths['shared'] +"/output", run_files=run_files, cut_files=cut_files,
                   package_base_dir=os.path.dirname(students_irlc_tools), censor_files=censor_files, strict=strict,
                   include_solutions=hw.get('include_solutions', False),
                   verbose=verbose)

        if censor_files:
            with open(paths['shared'] + f"/output/lines_{hw['week']}.txt", 'w') as f:
                f.write(str(n))


    if fix_shared_files:
        if verbose:
            print("> Homework fixed, copying shared files...")
        fix_all_shared_files(paths=paths, compile_templates=False, verbose=verbose, dosvg=convert_svg)

    if verbose:
        print("> Removing excluded files from students gitlab directory...")
    # for f in exclude + ["lectures"]:
    for l in list(Path(students_irlc_tools).glob('**/*')):
        if not os.path.exists(l):  # May have been removed as part of a subtree
            continue
        m = [ ex for ex in exclude if fnmatch.fnmatch(l, ex)]
        if len(m) > 0:
            if os.path.isdir(l):
                shutil.rmtree(l)
            else:
                os.remove(l)

    for f in glob.glob(public_irlc_tools +"/../requirements*.txt"):
        if "full" in os.path.basename(f):
            continue
        os.path.basename(f) # Copy requirements and all simiarly named files.
        shutil.copy(f, students_irlc_tools +"/../"+os.path.basename(f))
    # shutil.copy(public_irlc_tools +"/../requirements_conda.txt", students_irlc_tools +"/../requirements_conda.txt")
    # Don't think we need this anymore. Why copy docs if they cannot be compiled anyway? Perhaps for the complete docs?
    if include_docs:
        if os.path.isdir(students_irlc_tools + "/../docs"):
            shutil.rmtree(students_irlc_tools + "/../docs")
        # if not censor_files:
        shutil.copytree(public_irlc_tools + "/../docs", students_irlc_tools + "/../docs")
        # Fix reference to output.
        snip_dir(public_irlc_tools + "/../docs/source", students_irlc_tools + "/../docs/source", run_files=False, cut_files=False, censor_files=False, references=info['references'], verbose=verbose)
    if verbose:
        print("> All student file stuff completed.")



def fix_hw(paths, info, hw, out, output_dir, run_files=False, cut_files=False, censor_files=True,
           include_solutions=False,
           package_base_dir=None,  # When runing files using #!o, this specify the base directory of the  package the files resides in. Can be None.
           verbose=True,
           **kwargs):

    n, cutouts = snip_dir(source_dir=hw['base'], dest_dir=out, output_dir=output_dir, exclude=hw['exclusion'], references=info['references'],
                 run_files=run_files, cut_files=cut_files, license_head=info.get('code_copyright', None),
                 censor_files=censor_files,verbose=verbose,package_base_dir=package_base_dir)

    if "tests" in hw['base']:
        print(hw)
        print("Doing the base.")

    if include_solutions:
        wk = hw['base'].split("/")[-1]
        sp = paths['02450students'] + "/solutions/"
        if not os.path.exists(sp):
            os.mkdir(sp)
        sp = sp + wk
        if os.path.isdir(sp):
            shutil.rmtree(sp)
        # if not os.path.exists(sp):
        os.mkdir(sp)

        for f, cut in cutouts.items():
            if len(cut) > 0:
                fname = os.path.basename(f)
                sols = []
                stext = ["\n".join(lines) for lines in cut]
                for i, sol in enumerate(stext):
                    sols.append((sol,))
                    sout = sp + f"/{os.path.basename(fname)[:-3]}_TODO_{i + 1}.py"
                    wsol = True
                    print(sout, "(published)" if wsol else "")
                    if wsol:
                        with open(sout, "w") as f:
                            f.write(sol)

    return n