from genericpath import exists
import os, platform, logging, tempfile, shutil, time, importlib, re
from .model import A2MLModel


class A2MLProcessor:
    def __init__(self, args):
        self.start_time = time.time()  # time.process_time()
        self.args = args
        self.generator_root = os.path.abspath(os.path.join(__file__, "../.."))
        self.sdk_root = os.path.abspath(os.path.join(self.generator_root, ".."))
        self.verbose = self.get_arg("verbose", False)
        self.input_folders = None
        self.dependencies = None
        self.output_folder = None
        self.generator = None
        self.message_version = None
        self.model = None

        # initialize logger
        logging.basicConfig(
            level=logging.DEBUG if self.verbose else logging.INFO, format="\033[0;37m[A2DL] %(levelname)s: %(message)s"
        )
        logging.info("Python version: %s" % platform.python_version())

    def run(self):
        # initialize the builder
        self.initialize_configuration()
        self.initialize_environment()
        # parse the input files
        self.parse()
        # build the specifed modules
        self.generate()
        # package the archive
        self.export()
        # log completion time
        logging.info("SUCCESS! elapsed time: %s" % self.get_duration_str(time.time() - self.start_time))

    def get_arg(self, name, default=None):
        return self.args[name] if name in self.args else default

    def initialize_configuration(self):
        arg_input = self.get_arg("input")
        if not arg_input:
            raise Exception("Input argument rquired!")
        # set the input folder list
        self.input_folders = [os.path.abspath(next) for next in arg_input]
        for next in self.input_folders:
            if not (os.path.exists(next) and os.path.isdir(next)):
                raise Exception("Invalid input argument: %s" % next)
        # set the dependency folder list
        arg_dependencies = self.get_arg("dependencies")
        self.dependencies = [os.path.abspath(next) for next in arg_dependencies]
        for next in self.dependencies:
            if not (os.path.exists(next) and os.path.isdir(next)):
                raise Exception("Invalid dependencies argument: %s" % next)

        # dynamically load parser module
        arg_parser = self.get_arg("parser")
        if not arg_parser:
            raise Exception("Invalid parser argument: %s" % arg_parser)
        self.parser_module = importlib.import_module("A2ML.%s.parser" % arg_parser.lower())

        # dynamically load generator module
        arg_generator = self.get_arg("generator")
        if not arg_generator:
            raise Exception("Invalid generator argument: %s" % arg_generator)
        self.generator_module = importlib.import_module("A2ML.%s.generator" % arg_generator.lower())

        # aasb version
        arg_message_version = self.get_arg("message_version")
        if not arg_message_version:
            raise Exception("Message version not specified.")
        elif not re.match("^\d+\.\d+(\.\d+)?(-.+)?$", arg_message_version):
            raise Exception(f"Message version is not valid: {arg_message_version}")
        self.message_version = arg_message_version

        # if we are creating the output files then validate the output folder
        if not self.get_arg("no_output", False):
            arg_output = self.get_arg("output", None)
            if not arg_output:
                raise Exception("Output directory must be specified!")
            output_path = os.path.abspath(arg_output)
            if os.path.exists(output_path):
                if not os.path.isdir(output_path):
                    raise Exception("Output path must be a directory!")
            else:
                parent_path = os.path.abspath(os.path.join(output_path, ".."))
                if not (os.path.exists(parent_path) and os.path.isdir(parent_path)):
                    raise Exception("Output directory must exist!")
            self.output_folder = output_path

        # create the temporary output folder
        self.temp_output_folder = tempfile.TemporaryDirectory().name

    def initialize_environment(self):
        pass

    def parse(self):
        # create the model
        self.model = A2MLModel(self.message_version)
        # create the parser
        parser = self.parser_module.Parser(self.model)
        # load data definitions from all of the specified input folders
        for next in self.dependencies:
            parser.parse(next)
        for next in self.input_folders:
            parser.parse(next, True)

    def generate(self):
        # create the generator
        generator = self.generator_module.Generator(self.model)
        generator.generate(self.temp_output_folder)

    def export(self):
        if self.output_folder:
            for (path, dirnames, filenames) in os.walk(self.temp_output_folder):
                for name in filenames:
                    src_path = os.path.join(path, name)
                    rel_path = os.path.relpath(src_path, self.temp_output_folder)
                    dest_path = os.path.join(self.output_folder, rel_path)
                    dest_parent = os.path.abspath(os.path.join(dest_path, ".."))
                    os.makedirs(dest_parent, exist_ok=True)
                    shutil.copy(src_path, dest_path)

    def get_duration_str(self, dur_secs):
        hours, remainder = divmod(dur_secs, 3600)
        minutes, seconds = divmod(remainder, 60)
        if minutes < 1:
            return "%ss" % int(seconds)
        elif hours < 1:
            return "%sm %ss" % (int(minutes), int(seconds))
        else:
            return "%sh %sm %ss" % (int(hours), int(minutes), int(seconds))
