#!/usr/bin/env python3
import json
import os
import pathlib
import shutil
import subprocess
import sys

import necxxt
import necxxt.dependencies
import necxxt.utils

__executable__ = os.path.basename(__file__)

__env__ = {
    "CXX": "docker run -ti -v {}:/necxxt --rm necxxt/clang".format(os.getcwd()),
    "CXXFLAGS": "-std=c++2a -fmodules-ts"
}


def help(args):
    print()
    print("Usage: {} <command>".format(__executable__))
    print()
    print("where <command> is one of:")
    print("    build, help, install")
    print()
    print("Specify configs in the ini-formatted file:")
    print("    {}".format(os.path.join(
        pathlib.Path.home(), ".{}rc".format(__executable__))))
    print("or on the command line via: {} < command > --key value".format(__executable__))
    print("Config info can be viewed via: {} help config".format(__executable__))
    print()
    print("{}@{} {}".format(__executable__, necxxt.__version__, __file__))


def runCompiler(cmds):
    proc = subprocess.Popen(cmds)
    proc.communicate()

    if proc.returncode != 0:
        exit(proc.returncode)


def precompileModules(args):
    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".cxxm"]):
        i = f
        d = os.path.dirname(f)
        f = os.path.basename(f)
        name, extension = os.path.splitext(f)
        o = os.path.join("build", d, name + ".pcm")
        pathlib.Path(os.path.dirname(o)).mkdir(parents=True)

        modules = []
        for _f in necxxt.utils.listFiles(os.getcwd(), filters=[".pcm"]):
            if _f not in modules:
                modules.append(_f)

        cmds = __env__["CXX"].split(" ")
        cmds.extend(__env__["CXXFLAGS"].split(" "))
        cmds.append("--precompile")
        cmds.append(i)

        for m in modules:
            cmds.append("-fmodule-file={}".format(m))

        cmds.append("-o")
        cmds.append(o)

        print(" ".join(cmds))
        runCompiler(cmds)


def compileModules(args):
    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".pcm"]):
        i = f
        d = os.path.dirname(f)
        f = os.path.basename(f)
        name, extension = os.path.splitext(f)
        o = os.path.join(d, name + ".pcm.o")

        cmds = __env__["CXX"].split(" ")
        cmds.extend(__env__["CXXFLAGS"].split(" "))
        cmds.append("-c")
        cmds.append(i)
        cmds.append("-o")
        cmds.append(o)

        print(" ".join(cmds))
        runCompiler(cmds)


def compileSources(args):
    modules = []
    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".pcm"]):
        if f not in modules:
            modules.append(f)

    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".cxx"]):
        i = f
        d = os.path.dirname(f)
        f = os.path.basename(f)
        name, extension = os.path.splitext(f)
        o = os.path.join("build", d, name + ".cxx.o")
        pathlib.Path(os.path.dirname(o)).mkdir(parents=True)

        cmds = __env__["CXX"].split(" ")
        cmds.extend(__env__["CXXFLAGS"].split(" "))
        cmds.append("-c")
        cmds.append(i)

        for m in modules:
            cmds.append("-fmodule-file={}".format(m))

        cmds.append("-o")
        cmds.append(o)

        print(" ".join(cmds))
        runCompiler(cmds)


def buildStatic(args):
    cmds = __env__["CXX"].split(" ")
    cmds.extend(__env__["CXXFLAGS"].split(" "))

    module_paths = []
    objects = []
    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".pcm.o"]):
        d = os.path.dirname(f)
        if d not in module_paths:
            module_paths.append(d)

        if f not in objects:
            objects.append(f)

    for f in necxxt.utils.listFiles(os.getcwd(), filters=[".cxx.o"]):
        if f not in objects:
            objects.append(f)

    for p in module_paths:
        cmds.append("-fprebuilt-module-path={}".format(p))

    for o in objects:
        cmds.append(o)

    cmds.append("-o")

    module = necxxt.utils.readModule()
    cmds.append(os.path.join("build", module["name"]))
    cmds.append("-static")

    print(" ".join(cmds))
    runCompiler(cmds)


def build(args):
    if os.path.isdir("build"):
        shutil.rmtree("build")

    precompileModules(args)
    compileModules(args)
    compileSources(args)
    buildStatic(args)


def install(args):
    necxxt.dependencies.installDependencies()


def main(args):
    if len(args) == 0 or args[0] == "help":
        help(args)
    elif args[0] == "build":
        build(args)
    elif args[0] == "install":
        install(args)
    else:
        help(args)
        exit(1)


if __name__ == "__main__":
    args = sys.argv[1:]

    main(args)
