# -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
"""
Dependencies:
  - from: Git
    role: Find commit hash and/or date
  - from: Bash
    role: Run external commands to create replacement text
"""
import json
import os
import re
import shlex
from subprocess import run, PIPE
import sys
from .error import KiPlotConfigurationError
from .misc import FAILED_EXECUTE, W_EMPTREP
from .optionable import Optionable
from .pre_base import BasePreFlight
from .gs import GS
from .macros import macros, document, pre_class  # noqa: F401
from . import log

logger = log.get_logger()
re_git = re.compile(r'([^a-zA-Z_]|^)(git) ')


class KiCadVariable(Optionable):
    """ KiCad variable definition """
    def __init__(self):
        super().__init__()
        self._unkown_is_error = True
        with document:
            self.name = ''
            """ Name of the variable. The `version` variable will be expanded using `${version}` """
            self.variable = None
            """ {name} """
            self.text = ''
            """ Text to insert instead of the variable """
            self.command = ''
            """ Command to execute to get the text, will be used only if `text` is empty.
                This command will be executed using the Bash shell.
                Be careful about spaces in file names (i.e. use "$KIBOT_PCB_NAME").
                The `KIBOT_PCB_NAME` environment variable is the PCB file and the
                `KIBOT_SCH_NAME` environment variable is the schematic file """
            self.before = ''
            """ Text to add before the output of `command` """
            self.after = ''
            """ Text to add after the output of `command` """
            self.expand_kibot_patterns = True
            """ Expand %X patterns. The context is `schematic` """

    def config(self, parent):
        super().config(parent)
        if not self.name:
            raise KiPlotConfigurationError("Missing variable name ({})".format(str(self._tree)))


class Set_Text_VariablesOptions(Optionable):
    """ A list of KiCad variables """
    def __init__(self):
        super().__init__()
        with document:
            self.variables = KiCadVariable
            """ [dict|list(dict)] Variables """

    def config(self, parent):
        super().config(parent)
        if isinstance(self.variables, type):
            self.variables = []
        elif isinstance(self.variables, KiCadVariable):
            self.variables = [self.variables]


@pre_class
class Set_Text_Variables(BasePreFlight):  # noqa: F821
    """ [dict|list(dict)] Defines KiCad 6 variables.
        They are expanded using ${VARIABLE}, and stored in the project file.
        This preflight replaces `pcb_replace` and `sch_replace` when using KiCad 6.
        The KiCad project file is modified """
    def __init__(self, name, value):
        f = Set_Text_VariablesOptions()
        f.set_tree({'variables': value})
        f.config(self)
        super().__init__(name, f.variables)

    @classmethod
    def get_doc(cls):
        return cls.__doc__, KiCadVariable

    @classmethod
    def get_example(cls):
        """ Returns a YAML value for the example config """
        return ("\n    - name: 'git_hash'"
                "\n      command: 'git log -1 --format=\"%h\" \"$KIBOT_PCB_NAME\"'"
                "\n      before: 'Git hash: <'"
                "\n      after: '>'")

    def apply(self):
        o = self._value
        if len(o) == 0:
            return
        if GS.ki5:
            raise KiPlotConfigurationError("The `set_text_variables` preflight is for KiCad 6 or newer")
        pro_name = GS.pro_file
        if not pro_name or not os.path.isfile(pro_name):
            raise KiPlotConfigurationError("Trying to define KiCad 6 variables but the project is missing ({})".
                                           format(pro_name))
        # Get the current definitions
        with open(pro_name, 'rt') as f:
            pro_text = f.read()
        data = json.loads(pro_text)
        text_variables = data.get('text_variables', {})
        GS.pro_variables = text_variables
        logger.debug("- Current variables: {}".format(text_variables))
        # Define the requested variables
        if GS.pcb_file:
            os.environ['KIBOT_PCB_NAME'] = GS.pcb_file
        if GS.sch_file:
            os.environ['KIBOT_SCH_NAME'] = GS.sch_file
        bash_command = None
        for r in o:
            text = r.text
            if not text and r.command:
                command = r.command
                if re_git.search(command):
                    git_command = self.ensure_tool('git')
                    command = re_git.sub(r'\1'+git_command.replace('\\', r'\\')+' ', command)
                if not bash_command:
                    bash_command = self.ensure_tool('Bash')
                cmd = [bash_command, '-c', command]
                logger.debug('Executing: '+shlex.join(command))
                result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
                if result.returncode:
                    logger.error('Failed to execute:\n{}\nreturn code {}'.format(r.command, result.returncode))
                    if result.stdout:
                        logger.error('stdout:\n{}'.format(result.stdout))
                    if result.stderr:
                        logger.error('stderr:\n{}'.format(result.stderr))
                    sys.exit(FAILED_EXECUTE)
                if not result.stdout:
                    logger.warning(W_EMPTREP+"Empty value from `{}`".format(r.command))
                text = result.stdout.strip()
            text = r.before + text + r.after
            logger.debug('  - ' + r.name + ' -> ' + text)
            text_variables[r.name] = text
        logger.debug("- Expanding %X patterns in variables")
        # Now that we have the variables defined expand the %X patterns (they could use variables)
        for r in o:
            if r.expand_kibot_patterns:
                text = text_variables[r.name]
                new_text = Optionable.expand_filename_both(self, text, make_safe=False)
                if text != new_text:
                    logger.debug('  - ' + r.name + ' -> ' + new_text)
                    text_variables[r.name] = new_text
        logger.debug("- New list of variables: {}".format(text_variables))
        # Store the modified project
        data['text_variables'] = text_variables
        GS.make_bkp(pro_name)
        with open(pro_name, 'wt') as f:
            f.write(json.dumps(data, sort_keys=True, indent=2))
        if GS.board:
            # Force a project and PCB reload
            GS.reload_project(pro_name)
