using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Windows;
using System.Xml;
using Aitex.Core.RT.Log;
using MECF.Framework.Common.Account;
using MECF.Framework.Common.Account.Extends;
using MECF.Framework.Common.Account.Permissions;
using MECF.Framework.Common.Equipment;
using MECF.Framework.UI.Client.CenterViews.Configs.Roles;
using MECF.Framework.UI.Client.CenterViews.Configs.SystemConfig;
using MECF.Framework.UI.Client.CenterViews.Editors;
using MECF.Framework.UI.Client.ClientBase;
using MECF.Framework.UI.Client.RecipeEditorLib.DGExtension.CustomColumn;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using MECF.Framework.UI.Core.Accounts;
using Sicentury.Core;

namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel
{
    public class RecipeFormatBuilder
    {

        /// <summary>
        /// 步骤起始编号。
        /// </summary>
        public const int STEP_INDEX_BASE = 1;

        public const string SPEC_COL_STEPNO = "StepNo";
        public const string SPEC_COL_STEPUID = "StepUid";
        public const string SPEC_COL_NAME = "Name";

        #region RecipeFormat.xml模板文件中使用的Node、Attribute名称

        private const string NAME_NODE_TABLE_RECIPE_FORMAT = "TableRecipeFormat";
        private const string NAME_NODE_CATALOG = "Catalog";
        private const string NAME_NODE_GROUP = "Group";
        private const string NAME_NODE_STEP = "Step";
        private const string NAME_ATTR_CHAMBER_TYPE = "RecipeChamberType";
        private const string NAME_ATTR_VERSION = "RecipeVersion";
        private const string NAME_ATTR_MODULE_NAME = "ModuleName";
        private const string NAME_ATTR_CONTROL_NAME = "ControlName";
        private const string NAME_ATTR_DISPLAY_NAME = "DisplayName";
        private const string NAME_ATTR_UNIT_NAME = "UnitName";
        private const string NAME_ATTR_DESC = "Description";

        private const string PATH_GROUP = $"{NAME_NODE_TABLE_RECIPE_FORMAT}/{NAME_NODE_CATALOG}/{NAME_NODE_GROUP}";

        #endregion

        #region Variables

        private readonly RecipeProvider _recipeProvider = new();

        #endregion

        #region Properties
        
        public List<PermissionControlItem> RecipeColumnPermSource
        {
            get;
            set;
        }

        public List<PermissionControlItem> RecipeStepPermSource
        {
            get;
            set;
        }

        public List<PermissionControlItem> ContentPermSource
        {
            get;
            set;
        }

        public RecipeStep Configs
        {
            get;
            set;
        }

        public RecipeStep OesConfig
        {
            get;
            set;
        }

        public RecipeStep VatConfig
        {
            get;
            set;
        }

        public RecipeStep BrandConfig
        {
            get;
            set;
        }

        public RecipeStep FineTuningConfig
        {
            get;
            set;
        }

        public string RecipeChamberType
        {
            get;
            set;
        }

        public string RecipeVersion
        {
            get;
            set;
        }

        public List<EditorDataGridTemplateColumnBase> Columns
        {
            get;
            set;
        }

        #endregion

        #region Methods

        private RecipeStep GetConfig(XmlNodeList nodes)
        {
            var configs = new RecipeStep(null);
            foreach (XmlNode node in nodes)
            {
                var childNodes = node.SelectNodes("Config");
                foreach (XmlNode configNode in childNodes)
                {
                    switch (configNode.Attributes["InputType"].Value)
                    {
                        case "TextInput":
                            var text = new StringParam(configNode.Attributes["Default"].Value)
                            {
                                ControlName = configNode.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                DisplayName = configNode.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                            };
                            configs.Add(text);
                            break;

                        case "Set3RatioInput":
                            var param = new Sets3RatioParam()
                            {
                                ControlName = configNode.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                DisplayName = configNode.Attributes[NAME_ATTR_DISPLAY_NAME].Value
                            };
                            configs.Add(param);
                            break;

                        case "DoubleInput":
                            var config = new DoubleParam(GlobalDefs.TryParseToDouble(configNode.Attributes["Default"].Value))
                            {
                                ControlName = configNode.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                DisplayName = configNode.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                            };
                            if (double.TryParse(configNode.Attributes["Max"].Value, out var max))
                            {
                                (config as DoubleParam).Maximun = max;
                            }
                            if (double.TryParse(configNode.Attributes["Min"].Value, out var min))
                            {
                                (config as DoubleParam).Minimun = min;
                            }
                            configs.Add(config);
                            break;

                        case "ReadOnlySelection":

                            var defValue = configNode.Attributes["Default"] != null
                                ? configNode.Attributes["Default"].Value
                                : "";

                            var xmlOptionList = configNode.SelectNodes("Item");
                            var listOption = new List<ComboxColumn.Option>();
                            foreach (XmlNode xmlOption in xmlOptionList)
                            {
                                var controlName = xmlOption.Attributes[NAME_ATTR_CONTROL_NAME].Value;
                                var displayName = xmlOption.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                                var opt = new ComboxColumn.Option(displayName, controlName, string.Empty);
                                listOption.Add(opt);
                            }

                            var comb = new ComboxParam(listOption, configNode.Attributes[NAME_ATTR_CONTROL_NAME].Value, string.Empty, defValue)
                            {
                                DisplayName = configNode.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                IsEditable = configNode.Attributes["InputType"].Value != "ReadOnlySelection",
                                EnableTolerance = configNode.Attributes["EnableTolerance"] != null && Convert.ToBoolean(configNode.Attributes["EnableTolerance"].Value),

                            };
                           
                            configs.Add(comb);
                            break;

                    }
                }
            }

            return configs;
        }

        /// <summary>
        /// 去除小数点、箭头、括号（含括号里的内容）、横杠
        /// </summary>
        /// <param name="displayName"></param>
        /// <param name="bracket">新增参数，括号及其内容可以不去除</param>
        /// <returns></returns>
        private string strAbc(string displayName, bool bracket = true)
        {
            if (Regex.IsMatch(displayName.Substring(0, 1), @"^[+-]?\d*[.]?\d*$"))
            {
                displayName = displayName.Remove(0, displayName.IndexOf(".") + 1); //去除序号
            }

            displayName = displayName.Trim().Replace(" ", "").Replace(".", "").Replace("->", "_").Replace("-", "");
            if (bracket)
            {
                if (displayName.Contains("(") && displayName.Contains(")"))
                {
                    displayName = displayName.Remove(displayName.IndexOf("("),
                        displayName.IndexOf(")") - displayName.IndexOf("(") + 1);
                }
            }

            return displayName;
        }

        public double GetDoubleValueByName(string module, string name)
        {
            var value = SystemConfigProvider.Instance.GetValueByName(module, name);
            double returnValue = 0;
            if (string.IsNullOrEmpty(value))
            {
                if (name.EndsWith(".Default"))
                {
                    returnValue = 0;
                }
                else if (name.EndsWith(".Min"))
                {
                    returnValue = 0;
                }
                else if (name.EndsWith(".Max"))
                {
                    returnValue = 10000;
                }
            }
            else
            {
                returnValue = double.Parse(value);
            }
            return returnValue;
        }

        public string GetStringValueByName(string module, string name)
        {
            return SystemConfigProvider.Instance.GetValueByName(module, name);
        }
        
        /// <summary>
        /// 获取RecipInfo的Name信息
        /// </summary>
        /// <param name="path"></param>
        /// <param name="module"></param>
        /// <param name="defaultCellEnable"></param>
        /// <returns></returns>
        public List<PermissionControlItem> GetRecipeColumnPermSource(string path, string module = "PM1", bool defaultCellEnable = true)
        {
            var str = _recipeProvider.GetRecipeFormatXml(path, module);
            var doc = new XmlDocument();

            try
            {
                doc.LoadXml(str);

                var nodeRoot = doc.SelectSingleNode(NAME_NODE_TABLE_RECIPE_FORMAT);
                RecipeChamberType = nodeRoot.Attributes[NAME_ATTR_CHAMBER_TYPE].Value;
                RecipeVersion = nodeRoot.Attributes[NAME_ATTR_VERSION].Value;
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                return null;
            }

            RecipeColumnPermSource = new List<PermissionControlItem>();

            var nodes = doc.SelectNodes(PATH_GROUP);
            foreach (XmlNode node in nodes)
            {
                var childNodes = node.SelectNodes(NAME_NODE_STEP);
                foreach (XmlNode step in childNodes)
                {
                    var name = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                    var id = step.Attributes[NAME_ATTR_CONTROL_NAME].Value;
                    RecipeColumnPermSource.Add(new PermissionControlItem(name, RolePermissionMapper.None, id));
                }
            }

            return RecipeColumnPermSource;
        }

        /// <summary>
        /// 获取StepInfo的No信息
        /// </summary>
        /// <returns></returns>
        public List<PermissionControlItem> GetRecipeStepPermSource()
        {
            RecipeStepPermSource = new ();

            for (var i = 0; i < 50; i++)
            {
                var name = "Step" + (i + 1);
                var id = (i + 1).ToString();
                RecipeStepPermSource.Add(new PermissionControlItem(name, RolePermissionMapper.None, id));
            }

            return RecipeStepPermSource;
        }

        /// <summary>
        /// 获取ContentInfo的Name信息
        /// </summary>
        /// <param name="path"></param>
        /// <param name="module"></param>
        /// <param name="defaultCellEnable"></param>
        /// <returns></returns>
        public List<PermissionControlItem> GetContentPermSource(string path,string module = "PM1", bool defaultCellEnable = true)
        {
            var str = _recipeProvider.GetRecipeFormatXml(path, module);
            var doc = new XmlDocument();

            try
            {
                doc.LoadXml(str);

                var nodeRoot = doc.SelectSingleNode(NAME_NODE_TABLE_RECIPE_FORMAT);

                RecipeChamberType = nodeRoot.Attributes[NAME_ATTR_CHAMBER_TYPE].Value;

                RecipeVersion = nodeRoot.Attributes[NAME_ATTR_VERSION].Value;
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                return null;
            }

            ContentPermSource = new List<PermissionControlItem>();

            var nodesCatalog = doc.SelectNodes("TableRecipeFormat/Catalog");
            Debug.Assert(nodesCatalog != null, "No catalog nodes defined.");
            
            foreach (XmlNode catalog in nodesCatalog)
            {
                var catalogName = catalog.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                var nodeGroups = catalog.SelectNodes(NAME_NODE_GROUP);
                foreach (XmlNode group in nodeGroups)
                {
                    var groupName = group.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                    var nodeContents = group.SelectNodes("Content");
                    foreach (XmlNode content in nodeContents)
                    {
                        var name = catalogName + "." + groupName + "." +
                                   content.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                        var description = content.Attributes[NAME_ATTR_DESC]?.Value ?? "";
                        ContentPermSource.Add(new PermissionControlItem(name, RolePermissionTrueFalseMapper.No,
                            description: description));
                    }
                }
            }

            return ContentPermSource;
        }
        
        /// <summary>
        /// 从指定的RecipeFormat.xml文件创建Recipe格式。
        /// </summary>
        /// <param name="path"></param>
        /// <param name="module"></param>
        /// <param name="defaultCellEnable"></param>
        /// <returns></returns>
        public List<EditorDataGridTemplateColumnBase> Build(string chamberId,  string module, bool defaultCellEnable = true, RecipeType recipeType = RecipeType.Process)
        {
            //XML文档读取格式由 $"PM.{module}.RecipeConfig 改为 $"RecipeConfig    ，RecipeConfig参数放在Sic层级下，和PM同级别
            var str = _recipeProvider.GetRecipeFormatXml(chamberId, module);
            var doc = new XmlDocument();

            try
            {
                doc.LoadXml(str);

                var nodeRoot = doc.SelectSingleNode(NAME_NODE_TABLE_RECIPE_FORMAT);

                RecipeChamberType = nodeRoot.Attributes[NAME_ATTR_CHAMBER_TYPE].Value;

                RecipeVersion = nodeRoot.Attributes[NAME_ATTR_VERSION].Value;
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                return null;
            }

            var role = BaseApp.Instance.UserContext.Role;
            
            var columns = new List<EditorDataGridTemplateColumnBase>();

            EditorDataGridTemplateColumnBase col = null;
            var nodes = doc.SelectNodes(PATH_GROUP);
            foreach (XmlNode node in nodes)
            {
                var colExpander = new ExpanderColumn()
                {
                    DisplayName = node.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                    StringCellTemplate = "CellTemplateExpander",
                    StringHeaderTemplate = "ColumnHeaderTemplateExpander"
                };
                colExpander.Header = colExpander;
                columns.Add(colExpander);

                var childNodes = node.SelectNodes(NAME_NODE_STEP);
                foreach (XmlNode step in childNodes)
                {
                    //step number
                    if (step.Attributes[NAME_ATTR_CONTROL_NAME].Value == SPEC_COL_STEPNO)
                    {
                        col = new StepColumn()
                        {
                            DisplayName = "Step",
                            UnitName = "",
                            ControlName = "StepNo",
                            StringCellTemplate = "CellTemplateStepNo",
                            StringHeaderTemplate = "ColumnHeaderTemplateStepNo",
                            IsEnable = defaultCellEnable,
                            IsReadOnly = true
                        };
                    }
                    else if (step.Attributes[NAME_ATTR_CONTROL_NAME].Value == SPEC_COL_STEPUID)
                    {
                        col = new TextBoxColumn()
                        {
                            ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                            ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                            DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                            UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                            StringCellTemplate = "CellTemplateDisplay",
                            StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                            IsReadOnly = true,
                            IsEnable = defaultCellEnable,
                            Visibility = Visibility.Collapsed
                        };
                    }
                    else
                    {
                        switch (step.Attributes["InputType"].Value)
                        {
                            
                            case "Set3RatioInput":
                                col = new RatioColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Default"),
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateSets3Ratio",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                    MaxSets = 3
                                };

                                break;

                            case "TextInput":
                                col = new TextBoxColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Default"),
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateText",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                break;

                            case "ReadOnly":
                                col = new TextBoxColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateText",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    IsReadOnly = true,
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                break;

                            case "NumInput":
                                col = new NumColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    InputMode = step.Attributes["InputMode"].Value,
                                    //Minimun = double.Parse(step.Attributes["Min"].Value),
                                    Minimun = GetDoubleValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Min"),
                                    //Maximun = double.Parse(step.Attributes["Max"].Value),
                                    Maximun = GetDoubleValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Max"),
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateNumber",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                break;

                            case "DoubleInput":
                                //MFC和PC的最大值读取MFC的设定
                                var displayText = new Regex(@"\(M\d+\)").Match(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)
                                    .Value;
                                var maxConfig =
                                    $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Max";
                                var minConfig =
                                    $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Min";
                                if (displayText.Contains("M"))
                                {
                                    maxConfig =
                                        $"PM.{module}.MFC.Mfc{displayText.Replace("(M", "").Replace(")", "")}.N2Scale";
                                }
                                else
                                {
                                    displayText = new Regex(@"\(PC\d+\)").Match(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)
                                        .Value;
                                    if (displayText.Contains("PC"))
                                    {
                                        maxConfig =
                                            $"PM.{module}.PC.PC{displayText.Replace("(PC", "").Replace(")", "")}.Scale";
                                    }
                                }

                                col = new DoubleColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    InputMode = step.Attributes["InputMode"].Value,
                                    Maximun = GetDoubleValueByName("PM", maxConfig),
                                    Minimun = GetDoubleValueByName("PM", minConfig),
                                    Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Default"),
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateNumber",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };

                                //strAbc此函数去除了括号及括号中的内容，所以在此处重新赋值，保留括号及其内容*********************
                                if (displayText.Contains("M")|| displayText.Contains("PC"))
                                {
                                    col.Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value, false)}.Default");
                                }
                                //******************************************************************************************


                                if (displayText.Contains("M"))
                                {
                                    ((DoubleColumn)col).Minimun = ((DoubleColumn)col).Maximun *GetDoubleValueByName("PM", $"PM.{module}.MFC.MinScale") / 100.0;
                                }
                                break;

                            case "DoubleTextInput":
                                //MFC和 PC 的最大值读取MFC的设定
                                displayText = new Regex(@"\(M\d+\)").Match(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value).Value;
                                maxConfig =
                                    $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Max";
                                minConfig =
                                    $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Min";
                                if (displayText.Contains("M"))
                                {
                                    maxConfig =
                                        $"PM.{module}.MFC.Mfc{displayText.Replace("(M", "").Replace(")", "")}.N2Scale";
                                }
                                else
                                {
                                    displayText = new Regex(@"\(PC\d+\)").Match(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)
                                        .Value;
                                    if (displayText.Contains("PC"))
                                    {
                                        maxConfig =
                                            $"PM.{module}.PC.PC{displayText.Replace("(PC", "").Replace(")", "")}.Scale";
                                    }
                                }

                                col = new DoubleColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    InputMode = step.Attributes["InputMode"].Value,
                                    Minimun = GetDoubleValueByName("PM", minConfig),
                                    Maximun = GetDoubleValueByName("PM", maxConfig),
                                    Default = "Hold",
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateNumberText",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                break;

                            case "EditableSelection":
                            case "ReadOnlySelection":
                                col = new ComboxColumn()
                                {
                                    //IsReadOnly = step.Attributes["InputType"].Value == "ReadOnlySelection",
                                    IsEditable = step.Attributes["InputType"].Value != "ReadOnlySelection",
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    //Default = step.Attributes["Default"] != null ? step.Attributes["Default"].Value : "",
                                    Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Default"),
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateComboBoxEx",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,


                                };
                                var items = step.SelectNodes("Item");
                                foreach (XmlNode item in items)
                                {
                                    var controlName = item.Attributes[NAME_ATTR_CONTROL_NAME].Value;
                                    var displayName = item.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                                    var opt = new ComboxColumn.Option(displayName, controlName);
                                    ((ComboxColumn)col).Options.Add(opt);
                                }
                                break;

                            case "FlowModeSelection":
                                col = new FlowModeColumn()
                                {
                                    //IsReadOnly = step.Attributes["InputType"].Value == "ReadOnlySelection",
                                    IsEditable = false,
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    //Default = step.Attributes["Default"] != null ? step.Attributes["Default"].Value : "",
                                    Default = GetStringValueByName("PM",
                                        $"PM.RecipeConfig.{strAbc(node.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.{strAbc(step.Attributes[NAME_ATTR_DISPLAY_NAME].Value)}.Default"),
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateComboBoxEx",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,


                                };
                                var flowMode = step.SelectNodes("Item");
                                foreach (XmlNode item in flowMode)
                                {
                                    var controlName = item.Attributes[NAME_ATTR_CONTROL_NAME].Value;
                                    var displayName = item.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                                    var opt = new ComboxColumn.Option(displayName, controlName);
                                    ((ComboxColumn)col).Options.Add(opt);
                                }
                                break;

                            case "LoopSelection":
                                col = new LoopComboxColumn()
                                {
                                    IsReadOnly = false,
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplateComboBoxEx",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                var options = step.SelectNodes("Item");
                                foreach (XmlNode item in options)
                                {
                                    var opt = new LoopComboxColumn.Option();
                                    opt.ControlName = item.Attributes[NAME_ATTR_CONTROL_NAME].Value;
                                    opt.DisplayName = item.Attributes[NAME_ATTR_DISPLAY_NAME].Value;
                                    ((LoopComboxColumn)col).Options.Add(opt);
                                }
                                
                                break;

                            case "PopSetting":
                                col = new PopSettingColumn()
                                {
                                    ModuleName = step.Attributes[NAME_ATTR_MODULE_NAME].Value,
                                    ControlName = step.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                    DisplayName = step.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                    UnitName = step.Attributes[NAME_ATTR_UNIT_NAME].Value,
                                    StringCellTemplate = "CellTemplateDisplay",
                                    StringCellEditingTemplate = "CellTemplatePopSetting",
                                    StringHeaderTemplate = "ColumnHeaderTemplateCommon",
                                    EnableConfig = step.Attributes["EnableConfig"] != null &&
                                                   Convert.ToBoolean(step.Attributes["EnableConfig"].Value),
                                    EnableTolerance = step.Attributes["EnableTolerance"] != null &&
                                                      Convert.ToBoolean(step.Attributes["EnableTolerance"].Value),
                                    IsEnable = defaultCellEnable,
                                };
                                break;
                        }
                    }

                    // Header属性指向列对象，使用HeaderTemplate模板渲染Header
                    col.Header = col;
                    
                    // 单元格是否可编辑？
                    var isCellEditable = step.Attributes["IsColumnEditable"]?.Value;
                    if (string.IsNullOrEmpty(isCellEditable) == false && isCellEditable.ToLower() == "false")
                    {
                        col.IsEditable = false;
                        col.IsReadOnly = true;
                        col.StringCellEditingTemplate = "";
                    }

                    // 默认情况下当前Col具有访问权限    StepUid不需要显示
                    col.Permission = col.ControlName != SPEC_COL_STEPUID ?  MenuPermissionEnum.MP_READ_WRITE : MenuPermissionEnum.MP_NONE;

                    // 如果当前Recipe类型为Process，则根据配置设置访问权限     2025-3-13HQQ, 取消权限功能，显示所有Step
                    //if (recipeType == RecipeType.Process)
                    //    SetPermission(col, role);

                    colExpander.ChildColumns.Add(col); // 将本列追加到Expander中
                    columns.Add(col);
                }
            }

            Columns = columns;

            var configs = new RecipeStep(null);
            
            nodes = doc.SelectNodes("TableRecipeFormat/ProcessConfig/Configs");
            foreach (XmlNode node in nodes)
            {
                var childNodes = node.SelectNodes("Config");
                foreach (XmlNode configNode in childNodes)
                {
                    
                    switch (configNode.Attributes["InputType"].Value)
                    {

                        case "DoubleInput":
                            var config = new DoubleParam(GlobalDefs.TryParseToDouble(configNode.Attributes["Default"].Value))
                            {
                                ControlName = configNode.Attributes[NAME_ATTR_CONTROL_NAME].Value,
                                DisplayName = configNode.Attributes[NAME_ATTR_DISPLAY_NAME].Value,
                                EnableConfig = configNode.Attributes["EnableConfig"] != null && Convert.ToBoolean(configNode.Attributes["EnableConfig"].Value),
                                EnableTolerance = configNode.Attributes["EnableTolerance"] != null && Convert.ToBoolean(configNode.Attributes["EnableTolerance"].Value),
                            };
                            if (double.TryParse(configNode.Attributes["Max"].Value, out var max))
                            {
                                (config as DoubleParam).Maximun = max;
                            }
                            if (double.TryParse(configNode.Attributes["Min"].Value, out var min))
                            {
                                (config as DoubleParam).Minimun = min;
                            }
                            configs.Add(config);
                            break;

                    }
                }
            }
            Configs = configs;

            BrandConfig = GetConfig(doc.SelectNodes("TableRecipeFormat/BrandConfig/Configs"));
            OesConfig = GetConfig(doc.SelectNodes("TableRecipeFormat/OesConfig/Configs"));
            VatConfig = GetConfig(doc.SelectNodes("TableRecipeFormat/VatConfig/Configs"));
            FineTuningConfig = GetConfig(doc.SelectNodes("TableRecipeFormat/FineTuningConfig/Configs"));

            return Columns;
        }

        /// <summary>
        /// 重新加载指定角色的列访问权限。
        /// </summary>
        /// <param name="role">指定的角色。</param>
        public void UpdateColumnsPermission(Role role)
        {
            foreach (var col in Columns)
            {
                SetPermission(col, role);
            }
        }

        /// <summary>
        /// 设置列权限。
        /// </summary>
        /// <remarks>
        /// 以下时机对列访问权限进行设置：
        /// <br/>
        /// 调用<see cref="RecipeFormatBuilder.Build"/>方法构建配方编辑器表格格式时；
        /// <br/>
        /// 当配方编辑器视图被激活时，可能列权限发生了变更，调用<see cref="UpdateColumnsPermission"/>方法更新列权限。
        /// </remarks>
        /// <param name="column"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        public static void SetPermission(EditorDataGridTemplateColumnBase column, Role role)
        {
            if (column == null)
                throw new ArgumentNullException(nameof(column));

            // StepNo列和StepUid列不受权限控制
            if (column.ControlName == SPEC_COL_STEPUID)
            {
                column.Permission = MenuPermissionEnum.MP_NONE;
            }
            else if (column.ControlName == SPEC_COL_STEPNO)
            {
                column.Permission = MenuPermissionEnum.MP_READ;
            }
            else
            {

                var mpKey = column.DisplayName.Trim(' ');
                column.Permission = role.PermissionControlItems.TryGetValue(mpKey, out var perm)
                    ? perm
                    : MenuPermissionEnum.MP_NONE;
            }
        }

        #endregion
    }
}
