#
# @file from https://github.com/Neutree/c_cpp_project_framework
# @author neucrack
#

import argparse
import os, sys, time, re, shutil
import subprocess
from multiprocessing import cpu_count

if not os.path.exists("CMakeLists.txt") or  not os.path.exists("main"):
    print("please run me at project folder!")
    exit(1)

try:
    sdk_path = sdk_path
except Exception:
    sdk_path = os.path.abspath("../../")
project_path = sys.path[0]
project_name = ""
project_cmake_path = project_path+"/CMakeLists.txt"
project_cmake_content = ""
with open(project_cmake_path) as f:
    project_cmake_content = f.read()
match = re.findall(r"{}(.*){}".format(r"project\(", r"\)"), project_cmake_content, re.MULTILINE|re.DOTALL)
if len(match) != 0:
    project_name = match[0]
    print(project_name)
if project_name == "":
    print("[ERROR] Can not find project name in {}".format(project_cmake_path))
    exit(1)


flash_dir = sdk_path+"/tools/flash"
if os.path.exists(flash_dir):
    sys.path.insert(1, flash_dir)
    from flash import parser as flash_parser
project_parser = argparse.ArgumentParser(description='build tool, e.g. `python project.py build`', prog="project.py", parents=[flash_parser])

project_parser.add_argument('--toolchain',
                        help='toolchain path ( absolute path )',
                        metavar='PATH',
                        default="")

project_parser.add_argument('--toolchain-prefix',
                        help='toolchain prefix(e.g. mips-elf-',
                        metavar='PREFIX',
                        default="")
project_parser.add_argument('--config_file',
                        help='config file path, e.g. config_defaults.mk',
                        metavar='PATH',
                        default="{}/config_defaults.mk".format(project_path))
project_parser.add_argument('--verbose',
                        help='for build command, execute `make VERBOSE=1` to compile',
                        action="store_true",
                        default=False)
cmd_help ='''project command'''
project_parser.add_argument("cmd",
                    help=cmd_help,
                    choices=["config", "build", "rebuild", "menuconfig", "clean", "distclean", "clean_conf", "flash"]
                    )

project_args = project_parser.parse_args()

cwd = sys.path[0]
os.chdir(cwd)

config_filename = ".config.mk"
gen_project_type = "Unix Makefiles"


config_content_old = ""


if os.path.exists(config_filename):
    with open(config_filename) as f:
        config_content_old = f.read()
header = "# Generated by config.py, DO NOT edit!\n\n"
config_content = header
update_config = False
if project_args.toolchain.strip() != "":
    if not os.path.exists(project_args.toolchain):
        print("config toolchain path error, not exitst:", project_args.toolchain)
        exit(1)
    update_config = True
    project_args.toolchain = project_args.toolchain.strip().replace("\\","/")
    config_content += 'CONFIG_TOOLCHAIN_PATH="'+project_args.toolchain+'"\n'
if project_args.toolchain_prefix.strip() != "":
    update_config = True
    project_args.toolchain_prefix = project_args.toolchain_prefix.strip().replace("\\","/")
    config_content += 'CONFIG_TOOLCHAIN_PREFIX="'+project_args.toolchain_prefix+'"\n'
config_content += '\n'
if update_config and config_content != config_content_old:
    with open(config_filename, "w") as f:
        f.write(config_content)
    if os.path.exists("build/config/global_config.mk"):
        os.remove("build/config/global_config.mk")
    print("generate config file at: {}".format(config_filename))

def update_toolchain_path(tool_chain_config_path, config_path, out_path):
    with open(tool_chain_config_path) as f:
        toolchain = f.read()
    toolchain = toolchain.split("\n")
    toolchain_conf = {}
    for t in toolchain:
        if not t.startswith("CONFIG_"):
            continue
        t = t.split("=")
        toolchain_conf[t[0]] = t[1][1:-1]
    with open(config_path) as f:
        configs = f.read().split("\n")
    find_count = 0
    for i, config in enumerate(configs):
        config = config.strip()
        if config.startswith("CONFIG_TOOLCHAIN_PATH"):
            configs[i] = '{}="{}"'.format("CONFIG_TOOLCHAIN_PATH", toolchain_conf['CONFIG_TOOLCHAIN_PATH'])
            find_count += 1
        if config.startswith("CONFIG_TOOLCHAIN_PREFIX"):
            configs[i] = '{}="{}"'.format("CONFIG_TOOLCHAIN_PREFIX", toolchain_conf['CONFIG_TOOLCHAIN_PREFIX'])
            find_count += 1
        if find_count == 2:
            break
    configs = "\n".join(configs)
    with open(out_path, "w") as f:
        f.write(configs)


# config
if project_args.cmd == "config":
    print("config complete")
# rebuild / build
elif project_args.cmd == "build" or project_args.cmd == "rebuild":
    print("build now")
    time_start = time.time()
    if not os.path.exists("build"):
        os.mkdir("build")
    os.chdir("build")
    if not os.path.exists("Makefile") or project_args.cmd == "rebuild":
        if not os.path.isabs(project_args.config_file):
            project_args.config_file = os.path.join(project_path, project_args.config_file)
        config_path = os.path.abspath(project_args.config_file)
        if not os.path.exists(config_path):
            print("config file path error:{}".format(config_path))
            exit(1)
        # udpate toolchain config from .config.mk config by --toolchain adn --toolchain_prefix arg 
        new_config_path = None
        config_filename = os.path.abspath(os.path.join("..", config_filename))
        if os.path.exists(config_filename):
            if not os.path.exists("config"):
                os.makedirs("config")
            new_config_path = os.path.abspath(os.path.join("config", "config.mk"))
            update_toolchain_path(config_filename, config_path, new_config_path)
        config_path = config_path if not new_config_path else new_config_path
        # execute cmake command
        res = subprocess.call(["cmake", "-G", gen_project_type, "-DDEFAULT_CONFIG_FILE={}".format(config_path),  ".."])
        if res != 0:
            exit(1)
    if project_args.verbose:
        res = subprocess.call(["make", "VERBOSE=1"])
    else:
        res = subprocess.call(["make", "-j{}".format(cpu_count())])
    if res != 0:
        exit(1)

    time_end = time.time()
    print("==================================")
    print("time: {}".format(time.asctime(time.localtime())))
    print("build end, time last:%.2fs" %(time_end-time_start))
    print("==================================")
# clean
elif project_args.cmd == "clean":
    print("clean now")
    if os.path.exists("build"):
        os.chdir("build")
        p =subprocess.Popen(["make", "clean"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output, err = p.communicate("")
        res = p.returncode
        if res == 0:
            print(output.decode())
    print("clean complete")
# distclean    
elif project_args.cmd == "distclean":
    print("clean now")
    if os.path.exists("build"):
        os.chdir("build")
        p =subprocess.Popen(["make", "clean"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output, err = p.communicate("")
        res = p.returncode
        if res == 0:
            print(output.decode())
        os.chdir("..")
        shutil.rmtree("build")
    print("clean complete")
# menuconfig
elif project_args.cmd == "menuconfig":
    time_start = time.time()
    if not os.path.exists("build"):
        os.mkdir("build")
    os.chdir("build")
    if not os.path.exists("build/Makefile"):
        if not os.path.isabs(project_args.config_file):
            project_args.config_file = os.path.join(project_path, project_args.config_file)
        config_path = os.path.abspath(project_args.config_file)
        if not os.path.exists(config_path):
            print("config file path error:{}".format(config_path))
            exit(1)
        # udpate toolchain config from .config.mk config by --toolchain adn --toolchain_prefix arg 
        new_config_path = None
        config_filename = os.path.abspath(os.path.join("..", config_filename))
        if os.path.exists(config_filename):
            if not os.path.exists("config"):
                os.makedirs("config")
            new_config_path = os.path.abspath(os.path.join("config", "config.mk"))
            update_toolchain_path(config_filename, config_path, new_config_path)
        config_path = config_path if not new_config_path else new_config_path
        # execute cmake command
        res = subprocess.call(["cmake", "-G", gen_project_type, "-DDEFAULT_CONFIG_FILE={}".format(config_path),  ".."])
        if res != 0:
            exit(1)
    res = subprocess.call(["make", "menuconfig"])
    if res != 0:
        exit(1)
# flash
elif project_args.cmd == "flash":
    flash_file_path = os.path.abspath(sdk_path+"/tools/flash/flash.py")
    with open(flash_file_path) as f:
        exec(f.read())
# clean_conf
elif project_args.cmd == "clean_conf":
    print("clean now")
    # clean cmake config files
    if os.path.exists(config_filename):
        os.remove(config_filename)
    if os.path.exists("build/config/"):
        shutil.rmtree("build/config")
    # clean flash config file
    flash_file_path = os.path.abspath(sdk_path+"/tools/flash/flash.py")
    with open(flash_file_path) as f:
        exec(f.read())
    print("clean complete")
else:
    print("Error: Unknown command")
    exit(1)