# 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.

import os
import inspect
from abc import ABC,abstractmethod
import logging
from logging import Logger as SysLogger
from typing import List

FORMATTER = logging.Formatter("%(levelname)-8s%(filename)s:%(lineno)d  %(message)s")

class Logger:
    __instance = None
    @staticmethod
    def getInstance():
        """ Static access method. """
        if Logger.__instance == None:
            Logger()
        return Logger.__instance

    @staticmethod
    def getLogger() -> logging.Logger:
        return Logger.getInstance().log

    def __init__(self):
        """ Virtually private constructor. """
        if Logger.__instance != None:
            raise Exception("This class is a singleton!")
        else:
            Logger.__instance = self
        self.log = SysLogger.manager.getLogger('pymaketool')
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(FORMATTER)
        self.log.addHandler(console_handler)
        self.log.setLevel(os.environ.get('LOGLEVEL', 'NOTSET'))

__log = Logger().getLogger()

class MKVARS():
    LD      = "$(LD)"
    OBJECTS = '$(OBJECTS)' 
    LDFLAGS = '$(LDFLAGS)'
    OBJCOPY = '$(OBJCOPY)'
    NM          = '$(NM)'
    RANLIB      = '$(RANLIB)'
    STRINGS     = '$(STRINGS)'
    STRIP       = '$(STRIP)'
    CXXFILT     = '$(CXXFILT)'
    ADDR2LINE   = '$(ADD2LINE)'
    READELF     = '$(READELF)'
    CELFEDIT    = '$(ELFEDIT)'
    SIZE    = '$(SIZE)'
    TARGET  = '$(TARGET)'
    PROJECT = '$(PROJECT)'
    STATIC_LIBS = '$(SLIBS_NAMES)'

def MOD_PATH(wk):
    return wk['modPath']


class Define:
    """
    Direct define: { '__USE_FILE__': D('file.h') } => -D__USE_FILE__=file.h
    """
    def __init__(self, value):
        self.value = value
    def getDefine(self):
        if isinstance(self.value, str):
            return self.value
        else:
            return ''
    def __str__(self):
        return str(self.getDefine())
    def __repr__(self):
        return str(self.getDefine())


class AbstractMake(ABC):
    @abstractmethod
    def getProjectSettings(self, **kwargs) -> dict:
        pass
    
    @abstractmethod
    def getTargetsScript(self, **kwargs) -> dict:
        pass

    @abstractmethod
    def getCompilerSet(self, **kwargs) -> dict:
        pass

    @abstractmethod
    def getCompilerOpts(self, **kwargs) -> dict:
        pass

    @abstractmethod
    def getLinkerOpts(self, **kwargs) -> dict:
        pass


def Makeclass(clazz):
    obj = clazz()
    if not isinstance(obj, AbstractMake):
        __log.warning(f"class \'{clazz.__name__}\' in Makefile.py not inheritance of pymakelib.AbstractMake")
    global ProjectInstance
    ProjectInstance = obj


def getProjectInstance() -> AbstractMake:
    try:
        _ = ProjectInstance
        return ProjectInstance
    except NameError:
        __log.debug("not Makeclass mode")
        pass
    return None

## More OOP for pymaketool

from pathlib import Path
import copy
from . import prelib as plib
from . import module
import sys

class Pymaketool():

    def __init__(self, workpath='./'):
        self.workpath = workpath
        sys.path.append(str(self.workpath))
        self.projSettings, self.compilerOpts, self.compilerSettings = plib.read_Makefilepy(self.workpath)


    def getModulesPaths(self) -> list:
        return list(Path(self.workpath).rglob('*[.|_]mk.py'))


    def readModules(self, modulesPaths) -> list:
        self.modules = []
        for filename in modulesPaths:
            mod = plib.readModule(filename, copy.deepcopy(self.compilerOpts), None)
            self.modules.extend(mod)
        return self.modules
    
    def read_modules(self, modulesPaths) -> List[module.AbstractModule]:
        self.modules = []
        for filename in modulesPaths:
            mod = plib.read_module(filename, copy.deepcopy(self.compilerOpts), None)
            self.modules.extend(mod)
        return self.modules