# ╔══════════════════════════════════════════════════════════════════════════╗
# ║ Copyright 2022. quinn.7@foxmail.com All rights reserved.                 ║
# ║                                                                          ║
# ║ Licensed under the Apache License, Version 2.0 (the "License");          ║
# ║ you may not use this file except in compliance with the License.         ║
# ║ You may obtain a copy of the License at                                  ║
# ║                                                                          ║
# ║ http://www.apache.org/licenses/LICENSE-2.0                               ║
# ║                                                                          ║
# ║ Unless required by applicable law or agreed to in writing, software      ║
# ║ distributed under the License is distributed on an "AS IS" BASIS,        ║
# ║ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ║
# ║ See the License for the specific language governing permissions and      ║
# ║ limitations under the License.                                           ║
# ╚══════════════════════════════════════════════════════════════════════════╝
"""
@ 模块     : Awaken任务预处理程序
@ 作者     : chenjiancheng
@ 邮箱     : quinn.7@foxmail.com
@ 编写时间 : 2022-08-10

@ 模块描述 :
    NULL

"""
from .structural import AwakenTask
from .structural import AwakenCase
from .structural import AwakenCodeLine
from ...baseic.log import LOG
from ...baseic.const import CONST
from ...baseic.keyword import KEYWORD
from ...baseic.error import AwakenTaskPretreatmentError
from ...baseic.message_recorder import MESSAGE_RECORDER
from ...kit.common import ENGINE_TYPE_FUNCTION_MAP
from ...kit.global_method_map import GLOBAL_METHOD_FUNCTION_MAP


class TaskPreprocessor:
    """
    [ Awaken任务预处理程序 ]

    ---
    描述:
        NULL

    """
    
    _task: AwakenTask
    """ 任务对象 """

    def pretreatment(self, task: AwakenTask):
        self._task = task
        try:
            self._init_namespace_by_task_type()
            self._running_preprocessing_instruction()

        except BaseException:
            import traceback
            LOG.error(traceback.format_exc())
            MESSAGE_RECORDER.template_print()

        return self._task


    def _init_namespace_by_task_type(self):
        """
        [ 初始化任务命名空间 ]

        ---
        描述:
            NULL

        """
        self._task.test_cases: dict = {}
        self._task.namespace: dict = {
            KEYWORD.Script.Namespace.TaskType: None,
            KEYWORD.Script.Namespace.TaskName: None,
            KEYWORD.Script.Namespace.TaskDocs: None
        }

        if self._task.task_type == CONST.Type.Task.Web:
            self._task.namespace[KEYWORD.Script.Namespace.TaskType]    = CONST.Type.Task.Web
            self._task.namespace[KEYWORD.Script.Decorator.BrowserType] = CONST.Type.Browser.Chromium

        elif self._task.task_type == CONST.Type.Task.Api:
            self._task.namespace[KEYWORD.Script.Namespace.TaskType]    = CONST.Type.Task.Api


    def _running_preprocessing_instruction(self):
        """
        [ 运行预处理指令 ]

        ---
        描述:
            NULL

        """
        # --------------------------------------------------------------------
        # 解析全局&&引擎方法映射字典
        # --------------------------------------------------------------------
        self._task.global_function_map = GLOBAL_METHOD_FUNCTION_MAP
        self._task.engine_function_map = ENGINE_TYPE_FUNCTION_MAP[self._task.task_type]

        # --------------------------------------------------------------------
        # 循环全局域语句
        # 用例域语句将被存放至 test_cases 字典中等待引擎解析
        # --------------------------------------------------------------------
        for codeline in self._task.basecodes:
            awaken_codeline = AwakenCodeLine(codeline)

            # 公域语句
            if awaken_codeline.region == CONST.Interpreter.KEYWORD_IDENT_SCOPE_UNIVERSE:
                
                # 赋值逻辑
                if awaken_codeline.type == CONST.Interpreter.CodeLineType.Give:
                    self._give_global_assignment(awaken_codeline.give_name, awaken_codeline.give_value)

                # 执行并赋值逻辑
                elif awaken_codeline.type == CONST.Interpreter.CodeLineType.RGive:
                    result = self._running_common_function(awaken_codeline.funtion_name, awaken_codeline.funtion_value)
                    self._give_global_assignment(awaken_codeline.give_name, result)

                # 执行逻辑
                elif awaken_codeline.type == CONST.Interpreter.CodeLineType.Run:
                    self._running_common_function(awaken_codeline.funtion_name, awaken_codeline.funtion_value)

                # 声明逻辑
                elif awaken_codeline.type == CONST.Interpreter.CodeLineType.SCase:
                    awaken_case = AwakenCase(
                        awaken_codeline.number,
                        awaken_codeline.case_name,
                        awaken_codeline.case_docs,
                    )
                    self._task.test_cases[awaken_case.name] = awaken_case

            # 私域语句
            else:

                # 装饰器逻辑
                if awaken_codeline.type == CONST.Interpreter.CodeLineType.SDecorator:
                    self._task.test_cases[awaken_codeline.region].decorator.update({awaken_codeline.decorator_key : awaken_codeline.decorator_value})

                # 执行并赋值逻辑
                elif awaken_codeline.type == CONST.Interpreter.CodeLineType.RGive:
                    self.verify_validity_method(awaken_codeline)

                # 执行逻辑
                elif awaken_codeline.type == CONST.Interpreter.CodeLineType.Run:
                    self.verify_validity_method(awaken_codeline)

                # 其他语句
                else:
                    self._task.test_cases[awaken_codeline.region].steps.append(awaken_codeline)


    def _give_global_assignment(self, give_name: str, give_value: str):
        """
        [ 公域赋值封装 ]

        ---
        描述:
            NULL
        
        """
        give_path_node = give_name.split(CONST.Interpreter.GrammarSymbol.VariablePath)
        give_path_node_number = len(give_path_node)
        current_node = self._task.namespace

        if give_path_node_number > 1:
            i = 1
            for node in give_path_node:
                if i == give_path_node_number:
                    current_node.update({node: give_value})
                else:
                    if node not in current_node.keys():
                        current_node.update({node: {}})
                    current_node = current_node[node]
                    i += 1
        else:
            self._task.namespace.update({give_name : give_value})


    def _running_common_function(self, name: str, paras: list):
        """
        [ 公域运行方法封装 ]

        ---
        描述:
            NULL
        
        """
        try:
            if name not in self._task.global_function_map.keys():
                error_message = CONST.Error.Interpreter.GrammarMethodWrongful('#NAME#', name)
                raise AwakenTaskPretreatmentError(error_message)

            # ----------------------------------------------------------------
            # 解析方法参数
            # ----------------------------------------------------------------
            if len(paras) > 0:
                new_function_value = []

                for value in paras:
                    if isinstance(value, str):
                        value_quote_symbol_count = value[0:2].count(CONST.Interpreter.GrammarSymbol.Quote)
                        para = value[value_quote_symbol_count:]
                        para_statement_symbol_count = para.count(CONST.Interpreter.GrammarSymbol.VariablePath)

                        if para_statement_symbol_count != 0:
                            current_node = self._task.namespace
                            para_nodes = para.split(CONST.Interpreter.GrammarSymbol.VariablePath)

                            if len(para_nodes) > 1:
                                for node in para_nodes:
                                    try:
                                        current_node = current_node[node]
                                    except KeyError:
                                        error_message = CONST.Error.Interpreter.GrammarNamespaceNodeNotExist('#NODE#', node)
                                        AwakenTaskPretreatmentError(error_message)
                            else:
                                try:
                                    current_node = current_node[para_nodes[0]]
                                except:
                                    error_message = CONST.Error.Interpreter.GrammarNamespaceNodeNotExist('#NODE#', node)
                                    AwakenTaskPretreatmentError(error_message)

                            new_function_value.append(current_node)

                        else:
                            # 如果 para 存在于全局命名空间中则取出, 否则视为普通字符串
                            if para in self._task.namespace.keys():
                                new_function_value.append(self._task.namespace[para])
                                continue
                            else:
                                new_function_value.append(para)
                                continue

                    else:
                        new_function_value.append(value)

            result = self._task.global_function_map[name](self._task.global_method, *new_function_value)
            return result

        except KeyError:
            error_message = CONST.Error.Interpreter.GrammarMethodWrongful('#NAME#', name)
            raise AwakenTaskPretreatmentError(error_message)


    def verify_validity_method(self, awaken_codeline):
        """
        [ 校验方法的有效性 ]

        ---
        描述:
            NULL

        """
        function_map = None
        if awaken_codeline.funtion_region == CONST.Interpreter.CodeLineScopet.Global:
            function_map = self._task.global_function_map.keys()
        else:
            function_map = self._task.engine_function_map.keys()

        if awaken_codeline.funtion_name in function_map:
            self._task.test_cases[awaken_codeline.region].steps.append(awaken_codeline)
        else:
            error_message = CONST.Error.Interpreter.GrammarMethodWrongful.replace('#LINE#', awaken_codeline.number).replace('#NAME#', awaken_codeline.funtion_name)
            raise AwakenTaskPretreatmentError(error_message)
            