import configparser
import json
import copy
import math

class ConfParse:
    def __init__(self, config_def_json_files_paths):
        self.config_def_json_files_paths = config_def_json_files_paths
        self.configs = {}
        for file in self.config_def_json_files_paths:
            with open(file, 'r') as f:
                jf = json.load(f)
                if "settings" in jf:
                    setting_json = jf["settings"]
                    for key, value in setting_json.items():
                        # print(key, value)
                        all_items = self._expend_children(value["children"])
                        for item_key, item_value in all_items.items():
                            self.configs[item_key] = item_value
                elif "overrides" in jf:
                    override_json = jf["overrides"]
                    for key, value in override_json.items():
                        if key in self.configs:
                            for item_key, item_value in value.items():
                                self.configs[key][item_key] = item_value
                        else:
                            self.configs[key] = value

    def _expend_children(self, config):
        tmp_c = {}
        for key,val in config.items():
            if "children" in val:
                tmp_c.update(self._expend_children(val["children"]))
                val.pop("children", None)
            tmp_c[key] = val
        return tmp_c

    def add_inst_cfg(self, inst_cfg_files_paths):
        copy_settings = copy.deepcopy(self.configs)
        config = configparser.ConfigParser()
        for file in inst_cfg_files_paths:
            config.read(file)
            if config.has_section('values'):
                for key, val in config.items('values'):
                    v = str(val)
                    if key not in copy_settings:
                        copy_settings[key] = {}
                    if v.startswith("="):
                        copy_settings[key]["value"] = v[1:]
                    else:
                        copy_settings[key]["value"] = v
        return copy_settings


    def parse_configs(self, settings):
        class ConfigStr(str):
            def __mul__(self, other): raise TypeError()
            def __rmul__(self, other): raise TypeError()
            def __add__(self, other): raise TypeError()
            def __radd__(self, other): raise TypeError()
            def __sub__(self, other): raise TypeError()
            def __rsub__(self, other): raise TypeError()
            def __truediv__(self, other): raise TypeError()
            def __rtruediv__(self, other): raise TypeError()
            def __pow__(self, other): raise TypeError()
            def __rpow__(self, other): raise TypeError()

        parsed_settings = copy.deepcopy(settings)
        
        last_unparsed = -1
        while True:
            unparsed = 0
            
            # 构建上下文环境变量上下文，用作eval的变量替换
            context = {}
            for k, v in parsed_settings.items():
                # 上下文里的变量取值：优先使用 value（如果有），否则使用 default_value
                if "value" in v:
                    val = v["value"]
                elif "default_value" in v:
                    val = v["default_value"]
                else:
                    val = None
                
                if isinstance(val, str):
                    if val.lower() == "true":
                        val = True
                    elif val.lower() == "false":
                        val = False
                    else:
                        try:
                            val = int(val)
                        except ValueError:
                            try:
                                val = float(val)
                            except ValueError:
                                val = ConfigStr(val)
                context[k] = val

            # 自定义函数实现
            def resolveOrValue(key):
                return context.get(key)
                
            def extruderValues(key):
                # 兼容简易的多挤出机查询逻辑，目前单挤出机环境下返回一个单元素列表
                return [context.get(key)]
                
            def extruderValue(extruder_position, key):
                # 对于单挤出机环境或者全局配置，直接忽略 extruder_position，返回指定的 key 对应的值
                return context.get(key)
                
            def defaultExtruderPosition():
                return 0
                    
            # 提供基础的运算函数支持
            builtin_funcs = {
                "max": max,
                "min": min,
                "abs": abs,
                "round": round,
                "int": int,
                "float": float,
                "bool": bool,
                "math": math,
                "resolveOrValue": resolveOrValue,
                "extruderValues": extruderValues,
                "extruderValue": extruderValue,
                "defaultExtruderPosition": defaultExtruderPosition
            }
                    
            for key, val_dict in parsed_settings.items():
                for field, field_val in val_dict.items():
                    # 仅对字符串进行尝试计算
                    if "type" == field:
                        continue
                        
                    if isinstance(field_val, str):
                        try:
                            # 如果是一个普通的纯字符串，比如分类名（如"Machine Type" ），eval可能会抛出SyntaxError或NameError
                            # 如果它是一个python表达式，则会被顺利计算出结果
                            evaluated = eval(field_val, {"__builtins__": builtin_funcs}, context)
                            # 避免将原本只是用来作类型声明的 "int"/"float" 纯字符串，被错误求值为内置 Python type class 打断 JSON 序列化
                            if evaluated != field_val and not isinstance(evaluated, type):
                                if val_dict.get("type") == "str" and not isinstance(evaluated, str):
                                    if isinstance(evaluated, (list, dict)):
                                        import json
                                        val_dict[field] = json.dumps(evaluated).replace(" ", "")
                                    else:
                                        val_dict[field] = str(evaluated)
                                else:
                                    val_dict[field] = evaluated
                        except Exception:
                            # 解析失败（例如是个普通英文字符串或者依赖还没被解开）则暂不做处理
                            unparsed += 1
                            
            # 如果在这一轮中未解析出的表达式数量不再减少，说明已经到达极限，跳出循环
            if unparsed == last_unparsed:
                break
            last_unparsed = unparsed
            
        return parsed_settings
        
    