import argparse
import os
import platform
import shlex
import subprocess
import time
from argparse import Namespace
import compiler
import errors
import tokenise

def run_merge(filename, flags: Namespace):
    # Initialize an empty list to hold the contents of the input file
    input_file_contents = []
    # Initialize an empty dictionary to hold the filenames we need to import
    import_filenames = {}
    # Initialize an empty list to hold the included files
    included_files = []

    outname = filename.split('.')[0]

    cmd = [
        "g++",
        "representation.cpp",
        "-std=c++2a",
        "-o3",
        "-w",
        "-xc++"
    ]

    output = flags.output
    outname = filename.split('.')[0]
    if output:
        outname = output
        cmd += ["-o", outname]

    elif platform not in ("Linux", "Darwin"):
        outname += '.exe'
        cmd += ["-o", outname]

    # Open the input file and read its contents into the input_file_contents list
    with open(filename, 'r') as f:
        input_file_contents = f.readlines()
    
    # Loop through the input file contents, looking for import and include statements
    for line in input_file_contents:
        if line.startswith('import'):
            # Extract the filename from the import statement
            filename = line.strip().split(' ')[1].replace(';', '') + '.rn'
            # Store the filename in the import_filenames dictionary
            import_filenames[filename] = True
        
        elif line.startswith('include'):
            # Extract the filename from the include statement
            filename = line.strip().split(' ')[1].replace(';', '')
            # Add the included file to the list of included files
            included_files.append(filename)
        
    # Add the included files to the command list
    for included_file in included_files:
        if included_file == "random":
            pass
        if included_file == "crypto":
            cmd.append('-lssl')
            cmd.append('-lsecp256k1')
            
        if included_file == "network":
            cmd.append('-llibcurl')
        else:
            cmd.append('-l{}'.format(included_file))
    
    # Open the merged file for writing
    with open('merged.rn', 'w') as merged_file:
        # Loop through the input file contents again, copying each line to the merged file
        for line in input_file_contents:
            # If the line is an import statement, open the corresponding file and append its contents to the merged file
            if line.startswith('import'):
                filename = line.strip().split(' ')[1].replace(';', '') + '.rn'
                with open(filename, 'r') as import_file:
                    merged_file.write('// ' + line)  # Add a commented out version of the import statement to the merged file
                    merged_file.write('\n') 
                    merged_file.write(import_file.read())
                    merged_file.write('\n') 
                    merged_file.write('\n') 
            else:
                merged_file.write(line)
    
    # Return the command list
    return cmd


def run_compile(cmd, flags: Namespace):
    if flags.version:
        print('Renloi Programming Language Version 1.15')
        exit()

    _platform = platform.system()

    filename = "merged.rn"

    if not os.path.isfile(flags.source_file):
        print((f"[ERROR]: '{flags.source_file}' not found. Please check the file name and try again"))
        os.remove("merged.rn")
        exit(1)

    info = flags.info
    run = flags.run
    rundelete = flags.rundelete
    print_tokens = flags.tokens
    raw_tokens = flags.rawtokens
    _print = flags.print
    output = flags.output
    verbose = flags.verbose
    check = flags.check

    if raw_tokens:
        print(tokenise.gettokens(filename=filename, verbose=verbose))
        os.remove("merged.rn")
        exit()

    if info:
        print(f"\n[INFO]: Started compiling {flags.source_file}\n")

    start_time = time.perf_counter()

    compiledcode, tokens = compiler.Compile(
        tokens=tokenise.gettokens(filename=filename, verbose=verbose),
        verbose=verbose,
        filename=filename
    ).iteratetokens()

    with open("representation.cpp", "w") as representation:
        representation.write(compiledcode)

    if print_tokens:
        print(tokens)
        os.remove("merged.rn")
        exit(1)

    if _print:
        print(compiledcode)
        os.remove("merged.rn")
        exit(1)

    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stdin=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )

    if _platform in ("Linux", "Darwin"):
        process.stdin.write(compiledcode.encode('utf-8'))
    else:
        process.stdin.write(str.encode(compiledcode))
        # not sure if this is actually needed on Windows
        process.stdin.close()

    output, error = process.communicate()

    end_time = time.perf_counter()


    if error != b"":
        errorstr = error.decode('utf-8')
        if not check:
            print((f"renloi: CompilationError:\n{errorstr}"))

        if check:
            print(
                (f"[INFO]: Errors in the compilation of '{flags.source_file}'; unsuccessful check"))

    if output == b"":
        flags.source_file = flags.source_file.split('.')[0].replace(
            "/", "\\\\") if _platform != "Linux" else flags.source_file.split('.')[0]
        if info and not check:
            print(
                f"[INFO]: Finished compiling '{flags.source_file}' in {round(end_time-start_time, 2)}s ({round(end_time-start_time, 2) * 1000}ms)\n")

        if check:
            print(
                f"[INFO] No errors in the compilation of '{flags.source_file}.rn'; successful check")
            os.remove(flags.source_file.split('.')[0]) if _platform == "Linux" else os.remove(
                flags.source_file + ".exe")
            os.remove("merged.rn")
            exit(0)

        if rundelete:
            os.system(
                f"./{flags.source_file.split('.')[0]}") if _platform == "Linux" else os.system(f".\{flags.source_file}.exe")
            os.remove(flags.source_file.split('.')[0]) if _platform == "Linux" else os.remove(
                flags.source_file + ".exe")

        elif run:
            os.system(
                f"./{flags.source_file.split('.')[0]}") if _platform == "Linux" else os.system(f".\{flags.source_file}.exe")


def main():
    parser = argparse.ArgumentParser(
                    prog = 'renloi',
                    description = 'This is the compiler for the renloi programming language',
                    epilog = 'Happy programming :)')

    parser.add_argument(
        '-i', '--info', action='store_true',
        help='Print additional information about compilation (such as time taken). Defaults to off.'
    )
    parser.add_argument(
        '-r', '--run', action='store_true',
        help='Run the generated executable automatically after compilation. Defaults to off.'
    )
    parser.add_argument(
        '-rd', '--rundelete', action='store_true',
        help='Run the generated executable automatically after compilation, and then delete it. Defaults to off.'
    )
    parser.add_argument(
        '-o', '--output', type=str, default='',
        help='The string specified after the flag will be the name of the generated executable. '
             'Defaults to the name of the Renloi file that was passed in.'
    )
    parser.add_argument(
        '-c', '--check', action='store_true',
        help='Check if the program will compile without actually compiling it. Defaults to off.'
    )
    parser.add_argument(
        '-t', '--tokens', action='store_true',
        help='Print tokens and exit. Defaults to off.'
    )
    parser.add_argument(
        '-rt', '--rawtokens', action='store_true',
        help='Print raw/unfiltered tokens and exit. Defaults to off.'
    )
    parser.add_argument(
        '-p', '--print', action='store_true',
        help='Print generated C++ code and exit. Defaults to off.'
    )
    parser.add_argument(
        '-v', '--verbose', action='store_true',
        help='Run with verbose logging. Defaults to off.'
    )
    
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('source_file', type=str, help='Renloi Source File', nargs='?')
    group.add_argument('--version', help='Display current version and exit. (no value needed)', action='store_true')

    parsed_args = parser.parse_args()
    if parsed_args.source_file:
        cmd = run_merge(parsed_args.source_file, flags=parsed_args)
        run_compile(cmd, flags=parsed_args)
        os.remove("merged.rn")
        os.remove("representation.cpp")

    else:
        run_compile(flags=parsed_args)
        os.remove("representation.cpp")

if __name__ == "__main__":
    main()
