# SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
# SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception

import os, sys
import logging

import dune.common.module as moduleInfo
from .exceptions import CompileError, ConfigurationError
from . import cmakebuilder as builderModule

logger = logging.getLogger(__name__)

def _constructBuilder():
    from .cmakebuilder import Builder, MakefileBuilder

    env_force = os.environ.get('DUNE_FORCE_BUILD', 'FALSE').upper()
    env_save  = os.environ.get('DUNE_SAVE_BUILD' , 'FALSE').upper()

    use_cmake_builder = os.environ.get('DUNE_PY_USE_CMAKEBUILDER' , 'FALSE').upper()

    if use_cmake_builder in ('1', 'TRUE'):
        # old cmake based builder
        return Builder(force=(env_force in ('1', 'TRUE')), saveOutput=env_save)
    else:
        # new Makefile based builder
        return MakefileBuilder(force=(env_force in ('1', 'TRUE')), saveOutput=env_save)

# builder object that builds the autogenerated Python modules
# (initialize is called on first autogenerated module to be loaded)
builder = _constructBuilder()

def initializeDunePy():
    """
    This function will call the necessary method on builder to ensure dune-py exists.
    """
    # initialize builder which will create dune-py if non-existent
    builder.initialize()

def setNoDependencyCheck():
    logger.debug("Switching off dependency check - modules will always be compiled")
    builderModule.noDepCheck = True
def setDependencyCheck():
    logger.debug("Switching on dependency check")
    builderModule.noDepCheck = False
def setFlags(flags="-g",noChecks=None):
    logger.debug("Using compile flags '"+flags+"'")
    builderModule.cxxFlags = flags
    if noChecks is True:
        setNoDependencyCheck()
    elif noChecks is False:
        setDependencyCheck()
def addToFlags(pre="",post="",noChecks=None):
    setFlags(pre+" " + moduleInfo.getCXXFlags() + " "+post, noChecks)

def unsetFlags(noChecks=None):
    logger.debug("Using compile flags from configuration of dune-py")
    builderModule.cxxFlags = None
    if noChecks is True:
        setNoDependencyCheck()
    elif noChecks is False:
        setDependencyCheck()
def reset():
    unsetFlags()
    setDependencyCheck()

def path(f):
    return os.path.dirname(os.path.realpath(f))+"/"

class Constructor(object):
    def __init__(self, args, body=None, extra=None):
        self.args = args
        self.body = body
        self.extra = [] if extra is None else extra

    def register(self, cls="cls"):
        if self.body is None:
            return cls + ".def( pybind11::init( " + self.args + " )" + "".join(", " + e for e in self.extra) + " );\n"
        if self.args:
            source = cls + ".def( pybind11::init( [] ( " + ", ".join(self.args) + " ) {"
        else:
            source = cls + ".def( pybind11::init( [] () {"
        source += "\n    ".join(self.body)
        source += "\n  } )" + "".join(", " + e for e in self.extra) + " );\n"
        return source

    def __str__(self):
        return self.register()


class Method(object):
    def __init__(self, name, args, body=None, extra=None):
        self.name = name
        self.args = args
        self.body = body
        if extra is None:
            self.extra = []
        else:
            self.extra = extra

    def register(self, cls="cls"):
        if self.body is None:
            return cls + ".def( \"" + self.name + "\", " + self.args + "".join(", " + e for e in self.extra) + " );\n"
        if self.args:
            source = cls + ".def(\"" + self.name + "\", [] ( " + ", ".join(self.args) + " ) {"
        else:
            source = cls + ".def( \"" + self.name + "\", [] () {"
        source += "\n    ".join(self.body)
        source += "\n  } " + "".join(", " + e for e in self.extra) + " );\n"
        return source

    def __str__(self):
        return self.register()

class Pickler(object):
    def __init__(self, getterBody, setterBody,
                 setterArgs="pybind11::tuple t",
                 extra=["pybind11::keep_alive<1,2>()","pybind11::prepend()"]):
        self.getterBody = getterBody
        self.setterBody = setterBody
        self.setterArgs = setterArgs
        if extra is None:
            self.extra = []
        else:
            self.extra = extra

    def register(self, cls="cls"):
        source = cls + ".def( pybind11::pickle(\n";
        source += "  [](const pybind11::object &self) {\n";
        source += self.getterBody + "}\n"
        source += ", [](" + self.setterArgs + ") {\n";
        source += self.setterBody + "}\n"
        source += ")" + "".join(", " + e for e in self.extra) + " );\n"
        return source

    def __str__(self):
        return self.register()

def requiredModules(outFile=None):
    path = "dune.generated."
    mods = [m[len(path):] for m in sys.modules.keys() if path in m]
    if outFile is not None:
        try:
            with open(outFile, "w") as f:
                print("\n".join(mods), file=f)
        except TypeError:
            print("\n".join(mods), file=outFile)
    return mods
