#!/usr/bin/python3
import sys
import json
import attrs
import pathlib
import argparse


class MyParser(argparse.ArgumentParser): 
   def error(self, message):
      sys.stderr.write('error: %s\n' % message)
      self.print_help()
      sys.exit(2)


@attrs.define
class Template():
    name: str
    alias: str
    language: str
    version: str
    author: str
    brief: str
    path: str
    mainfile: str
    files: list[str] = attrs.Factory(list)

    def compact_info(self, col_width=25):
        info = (f"\t{self.alias}{(col_width - len(self.alias)) * ' '}{self.name}{(col_width - len(self.name)) * ' '}{self.version}{(col_width - len(self.version)) * ' '}{self.language}")
        return info

    def full_info(self):
        print(f"{self.name} (alias {self.alias}) \t form version: {self.version}")
        print(f"{self.brief}")
        print(f"Language: {self.language}")
        print(f"Template by: {self.author}")
        print(f"Template location:{self.path}")
        print(f"Template files:")
        for f in self.files:
            print(f'\t{f}')

    def patch_make(self, source, dest, new_name):
        lines = []
        with open(source, "r") as f:
            for l in f.readlines():
                if l.startswith("BASENAME"):
                    l = f"BASENAME={new_name}\n"
                    lines.append(l)
                else:
                    lines.append(l)
        with open(dest, "w") as f:
            f.writelines(lines)

    def copy_files(self, destination, project_name):
        project_name = project_name.replace(" ", "_")
        dest = pathlib.Path(destination).joinpath(project_name)
        if dest.exists() and dest.is_dir():
            if len(list(dest.glob("*.*"))) > 0:
                raise ValueError(f"Destination folder ({str(dest.absolute())}) already exists and is not empty")
        dest.mkdir(exist_ok=True)
        src_path = pathlib.Path(self.path)
        # copy files
        for f in self.files:
            source_file = src_path.joinpath(f)
            dest_file = dest.joinpath(f)
            if f == self.mainfile:  # rename main file to match project name
                dest_file = dest.joinpath(source_file.with_stem(project_name).name)
                dest_file.write_bytes(source_file.read_bytes())
            elif f == "Makefile":
                self.patch_make(source_file, dest_file, project_name)
            else:
                dest_file.write_bytes(source_file.read_bytes())


def find_templates():
    templates = {}
    for file_path in pathlib.Path.cwd().glob("*"):
        if pathlib.Path.is_dir(file_path):
            if pathlib.Path.joinpath(file_path, "info.json").exists():
                with open(pathlib.Path.joinpath(file_path, "info.json")) as f:
                    data = json.load(f)
                if "templates" in data:
                    for t in data["templates"]:
                        if t["alias"] in templates:
                            print(UserWarning(f"A template with the alias {t['alias']} already exists! Ignoring template in path {str(file_path)}."))
                            continue
                        templates[t["alias"]] = (Template(**t, path=str(file_path)))
    return templates


def print_templates(templates, col_width=25):
    print("Available templates: ")
    print(f"\talias{(col_width - len('alias')) * ' '}name{(col_width - len('name')) * ' '}version{(col_width - len('language')) * ' '}language")
    for t in templates.keys():
        print(templates[t].compact_info(col_width))


def copy_template(args, templates):
    t = templates.get(args.template)
    if t is not None:
        t.copy_files(args.destination, args.name)


def create_parser():
    parser = MyParser(
        description="Creates a new DFG document according to latex templates.",
        prog='DFGdocs',
        epilog='By Jan Grewe, 2023, Free software, absolutely no warranty!')
    parser.add_argument("-l", "--list", action="store_true", help="List the available templates.")
    parser.add_argument("-d", "--destination", type=str, default=".", 
                        help="The destination folder as absolute or relative path.")
    parser.add_argument("-i", "--info", action="store_true", help="More info on a specific template.")
    parser.add_argument("template", nargs='?', type=str, help="The alias of the DFG document template (use -l or --list argument to get a list of templates).")
    parser.add_argument("name", nargs='?', type=str, help="The project name")
    return parser


def args_check(args, parser, templates):
    if args.list:
        print_templates(templates)
        sys.exit(0)

    if args.template is None:
        print(f"Error using DFGdocs. TEMPLATE argument is not defined!")
        parser.print_help()
        sys.exit(2)

    if args.name is None:
        print(f"Error using DFGdocs. NAME argument is not defined!")
        parser.print_help()
        sys.exit(2)

    if args.template not in templates:
        print(f"Error using DFGdocs. Template {args.template} is not defined!")
        print_templates(templates)
        sys.exit(0)

    if args.info and args.template is not None:
        templates[args.template].full_info()
        sys.exit(0)


def main():
    parser = create_parser()
    args = parser.parse_args()
    templates = find_templates()
    args_check(args, parser, templates)
    copy_template(args, templates)


if __name__ == "__main__":
    main()