#!/usr/bin/env python3

# Copyright (c) 2020, Ericson Joseph
# 
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
# 
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the documentation
#       and/or other materials provided with the distribution.
#     * Neither the name of pyMakeTool nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


from pathlib import Path
import importlib.util
import inspect
import sys
import os
import time
import subprocess
import json
import re
import copy
import argparse
from pymakelib import eclipse_cproject as cp
from pymakelib import preconts as K
from pymakelib import prelib as plib
from pymakelib import moduleignore
from pymakelib import eclipse_files
from pymakelib import make_files
from pymakelib import Addon


parser = argparse.ArgumentParser()
parser.add_argument('goal', type=str, help='Makefile command goal', default=None, nargs='?')
parser.add_argument('--init', type=str, help='initialize project', const=os.path.basename(os.getcwd()), dest='project_name', nargs='?')
parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0.12')
args = parser.parse_args()

goal = args.goal

if args.project_name:
    fproject = open('.project', 'w')
    print('Init {0} project'.format(args.project_name))
    fproject.write(eclipse_files.FILE_PROJECT.format(args.project_name))
    fproject.close()
    try:
        os.mkdir(K.PYMAKEPROJ)
    except:
        pass

    try:
        os.mkdir(K.ECLIPSE_SETTING)
    except Exception as e:
        print(e)

    fileset = [
        [K.PYMAKEPROJ + '/.cproject_template',eclipse_files.FILE_CPROJECT_TEMP],
        [K.PYMAKEPROJ + '/.language.settings_template', eclipse_files.FILE_LANGUAJE_SETTING_XML],
        ['Makefile', make_files.FILE_MAKEFILE],
        ['makefile.mk', make_files.FILE_MAKEFILE_MK],
        ['Makefile.py', make_files.FILE_MAKEFILE_PY]
    ]

    for f in fileset:
        try:
            fileout = open(f[0], 'x')
            fileout.write(f[1])
            fileout.close()
        except Exception as e:
            print(e)

    sys.exit()


USE_EXCLUDE_FOLDERS = True

# ------------------------------------------------------
# ------------------------------------------------------
# ------------------------------------------------------

if not os.path.exists(K.PYMAKEPROJ):
    print('Not a pymaketool project')
    sys.exit()

if not goal:
    print('pymaketool: error: Add a goal (target)')
    sys.exit()

modules = []
modulesPaths = []
ignoreModuleList = []

# Add project path to sys.path for users scripts
sys.path.append(str(os.getcwd()))

projSettings, compilerOpts, compilerSettings = plib.read_Makefilepy()

ignoreModuleList = moduleignore.readIgnoreFile()

modulesPaths = list(Path('./').rglob('*[.|_]mk.py'))
# Load modules
for filename in modulesPaths:
    mod = plib.readModule(filename, copy.deepcopy(compilerOpts), goal)
    modules.append(mod)

for ex in ignoreModuleList:
    for i, o in enumerate(modules):
        if o.filename == ex:
            del modules[i]
            break

# Write CSRC
srcsfile = open('srcs.mk', 'w')

includes = []

for mod in modules:
    print('Module: {}'.format(mod.filename))
    srcsfile.write("{}\n".format(plib.getLineSeparator('#', 52)))
    srcsfile.write("#{0:^50}#\n".format(str(mod.filename)))
    srcsfile.write("{}\n".format(plib.getLineSeparator('#', 52)))

    if mod.staticLib:
        mkkey = mod.staticLib.name.upper()
        srcsfile.write('{}_NAME = {}\n'.format(mkkey, mod.staticLib.name))
        srcsfile.write('{}_OUTPUT = {}\n'.format(mkkey, str(mod.staticLib.outputDir)))
        library = mod.staticLib.outputDir / Path('lib'+mod.staticLib.name+'.a')
        srcsfile.write('{}_AR = {}\n'.format(mkkey, str(library)))
        srcsfile.write('\n')

    prefixSrcs = ""
    if mod.staticLib:
        prefixSrcs = mod.staticLib.name.upper() + "_"
        
    for src in mod.srcs:
        if str(src).endswith('.c'):
            srcsfile.write("{}CSRC += {}\n".format(prefixSrcs, src))
        elif str(src).endswith('.cpp'):
            srcsfile.write("{}CXXSRC += {}\n".format(prefixSrcs, src))
        elif str(src).endswith('.s'):
            srcsfile.write("{}ASSRC += {}\n".format(prefixSrcs, src))

    srcsfile.write('\n')

    if not mod.staticLib:
        for dir in mod.getDirs():
            srcsfile.write("SRC_DIRS += {}\n".format(str(dir)))

    srcsfile.write('\n')

    for inc in mod.incs:
        srcsfile.write("INCS += -I{}\n".format(inc))
        includes.append(inc)

    srcsfile.write('\n')

    if mod.flags:
        if mod.staticLib:
            for src in mod.srcs:
                objs = str(src).replace('.c', '.o').replace('.s', '.o')
                mkkey = mod.staticLib.name.upper()
                outputObj = '$({}_OUTPUT)/'.format(mkkey) + str(objs)
                srcsfile.write("{} : CFLAGS = {}\n".format(
                    outputObj, plib.compilerOptsByModuleToLine(mod.flags)))
        else:
            for src in mod.srcs:
                objs = str(src).replace('.cpp', '.o')
                objs = objs.replace('.c', '.o')
                objs = objs.replace('.s', '.o')
                srcsfile.write("{} : CFLAGS = {}\n".format(
                    projSettings['FOLDER_OUT'] / str(objs), plib.compilerOptsByModuleToLine(mod.flags)))

    srcsfile.write('\n')

    if mod.staticLib:
        mkkey = mod.staticLib.name.upper()
        srcsfile.write('{0}_OBJECTS = $({0}_CSRC:%.c=$({0}_OUTPUT)/%.o) $({0}_CSRC:%.s=$({0}_OUTPUT)/%.o)\n'.format(mkkey))
        srcsfile.write('\n')
        srcsfile.write('$({0}_OUTPUT)/%.o: %.c\n\t$(call logger-compile-lib,"CC",\"{1}\",$<)\n\t@mkdir -p $(dir $@)\n\t$(CC) $(CFLAGS) $(INCS) -o $@ -c $<\n'.format(mkkey, library))
        srcsfile.write('\n')
        srcsfile.write('$({0}_AR): $({0}_OBJECTS)\n\t$(call logger-compile,"AR",$@)\n\t$(AR) -rc $@ $(filter %.o,$({0}_OBJECTS))'.format(mkkey))
        srcsfile.write('\n\n')
        srcsfile.write('SLIBS_NAMES += {}\n'.format(mod.staticLib.name))
        srcsfile.write('SLIBS_OBJECTS += {}\n'.format(library))
        if mod.staticLib.rebuild:
            srcsfile.write('\n')
            for src in mod.srcs:
                obj = str(src).replace('.c', '.o').replace('.s', '.o')
                mkkey = mod.staticLib.name.upper()
                outputObj = '$({}_OUTPUT)/'.format(mkkey) + str(obj)
                srcsfile.write("{} : .FORCE\n".format(outputObj))
            srcsfile.write('\n\n')

    srcsfile.write('\n')

srcsfile.close()

strIncs = []
aux = []

if compilerSettings['INCLUDES']:
    aux += compilerSettings['INCLUDES']
if includes:
    aux += includes

strIncs = [str(i) for i in aux]

listToExclude = []

if (USE_EXCLUDE_FOLDERS):
    
    allIncFoldes = []
    includes = [str(i) for i in includes]

    for filename in Path('.').rglob('*.h'):
        allIncFoldes.append(str(filename.parent))

    allIncFoldes = list(dict.fromkeys(allIncFoldes))
    allIncFoldes.sort()

    filtedist = []
    parent = ""
    for p in allIncFoldes:
        if parent == "":
            parent = p
            filtedist.append(p)
        elif(p.startswith(parent+"/")):
            None
        else:
            parent = p
            filtedist.append(p)

    allIncFoldes = filtedist

    p = re.compile('^(I|i)nc(lude)*$')

    for allinc in allIncFoldes:
        aux = allinc + '/'
        if not any(aux.startswith(a + "/") for a in includes):
            if not str(allinc).startswith('Test/ceedling') and not allinc == '.':
                auxpath = Path(allinc)
                if p.match(auxpath.name):
                    listToExclude.append(str(auxpath.parent))
                else:
                    listToExclude.append(allinc)
            
    listToExclude.append('Test/ceedling')
    

cproject_setting = {
    'C_SETTINGS': projSettings,
    'C_TARGETS': compilerOpts['TARGETS'],
    'C_INCLUDES': strIncs,
    'C_SYMBOLS': compilerOpts['MACROS'],
    'C_EXCLUDE': listToExclude
}

# Execute init function of plugins
for initFunc in Addon.initAddonFuncs:
    initFunc(cproject_setting, compilerSettings)

# Execute class of plugins
for obj in Addon.initAddonClass:
    p = obj(cproject_setting, compilerSettings)
    p.init()
