import os
import yaml
import json
from jinja2 import Template
from copy import deepcopy

header_msg1 = 'This file was automatically generated by jinjaroot. Do not edit directly. See the .jinjaroot dir.'
header_msg2 = 'This file was automatically generated by jinjaroot. Do not edit directly.'

def process_jinjaroot_dir(src_dirpath: str, dst_dirpath: str, template_data: dict):
    template_fnames = os.listdir(src_dirpath)
    for fname in template_fnames:
        template_path = f'{src_dirpath}/{fname}'
        fname2 = Template(fname).render(**template_data)
        dest_path = f'{dst_dirpath}/{fname2}'
        if os.path.isdir(template_path):
            if not os.path.exists(dest_path):
                os.mkdir(dest_path)
            process_jinjaroot_dir(src_dirpath=template_path, dst_dirpath=dest_path, template_data=template_data)
        elif os.path.isfile(template_path):
            with open(template_path, 'r') as f:
                template_code = f.read()
            template_stat = os.stat(template_path)
            if os.path.exists(dest_path):
                with open(dest_path, 'r') as f:
                    dest_code = f.read()
                dest_stat = os.stat(dest_path)
            else:
                dest_code = None
                dest_stat = None
            t = Template(template_code, keep_trailing_newline=True)
            gen_code = t.render(**template_data)
            gen_code = add_gen_header(fname, gen_code, header_msg1)
            if gen_code != dest_code:
                with open(dest_path, 'w') as f:
                    print(f'Writing {dest_path}')
                    f.write(gen_code)
            if (dest_stat is None) or (template_stat.st_mode != dest_stat.st_mode):
                os.chmod(dest_path, template_stat.st_mode)

def process_dir(dirpath: str, template_data: dict):
    jinja_root_yaml_path = f'{dirpath}/jinjaroot.yaml'
    jinja_root_json_path = f'{dirpath}/jinjaroot.json'
    jinjaroot_dirpath = f'{dirpath}/.jinjaroot'
    template_data2 = deepcopy(template_data)
    if os.path.isfile(jinja_root_yaml_path):
        with open(jinja_root_yaml_path, 'r') as f:
            x = yaml.safe_load(f)
            for k, v in x.items():
                template_data2[k] = v
    if os.path.isfile(jinja_root_json_path):
        with open(jinja_root_json_path, 'r') as f:
            x = json.load(f)
            for k, v in x.items():
                template_data2[k] = v
    if os.path.isdir(jinjaroot_dirpath):
        process_jinjaroot_dir(jinjaroot_dirpath, dirpath, template_data2)
    
    fnames = os.listdir(dirpath)
    for fname in fnames:
        file_path = f'{dirpath}/{fname}'
        if os.path.isfile(file_path):
            if ends_with(file_path, ['.ts', '.tsx', '.js', '.jsx', '.py', '.cfg', '.md', '.sh', '.yml', '.yaml']):
                template_path = file_path + '.j2'
                if os.path.exists(template_path):
                    with open(template_path, 'r') as f:
                        template_code = f.read()
                    with open(file_path, 'r') as f:
                        file_code = f.read()
                    t = Template(template_code, keep_trailing_newline=True)
                    gen_code = t.render(**template_data2)
                    gen_code = add_gen_header(fname, gen_code, header_msg2)
                    if gen_code != file_code:
                        with open(file_path, 'w') as f:
                            print(f'Writing {file_path}')
                            f.write(gen_code)
            if ends_with(file_path, ['.json']):
                substitute_template_path = file_path + '.substitute.j2'
                if os.path.exists(substitute_template_path):
                    with open(file_path, 'r') as f:
                        content1 = json.load(f)
                    with open(substitute_template_path, 'r') as f:
                        txt = f.read()
                        txt2 = Template(txt, keep_trailing_newline=True).render(**template_data2)
                        content2 = json.loads(txt2)
                    content1_new = _substitute_json(content1, content2)
                    gen_code = json.dumps(content1_new, indent=2)
                    with open(file_path, 'r') as f:
                        dest_code = f.read()
                    if gen_code != dest_code:
                        with open(file_path, 'w') as f:
                            print(f'Writing {file_path}')
                            f.write(gen_code)
        elif os.path.isdir(file_path):
            process_dir(dirpath=file_path, template_data=template_data2)

def _substitute_json(content1: dict, content2: dict):
    ret = deepcopy(content1)
    for k, v in content2.items():
        ret[k] = v
    return ret

def ends_with(fname, suffixes):
    for suf in suffixes:
        if fname.endswith(suf): return True
    return False

def insert_header(header: str, code: str):
    lines = code.splitlines()
    line1 = lines[0] if len(lines) > 0 else ''
    if line1.startswith('#!'):
        return code.replace(line1, line1 + '\n' + header)
    else:
        return header + code

def add_gen_header(fname: str, code: str, message: str):
    if ends_with(fname, ['.js', '.jsx', '.ts', '.tsx']):
        return insert_header(f'// {message}\n', code)
    elif ends_with(fname, ['.py', '.sh', '.yaml', '.yml', '.cfg']):
        return insert_header(f'# {message}\n', code)
    elif ends_with(fname, ['.md']):
        return insert_header(f'<!-- {message} -->\n', code)
    else:
        return code


def cli():
    template_data = {}
    process_dir('.', template_data)