using System;
using System.Collections.Generic;
using System.Xml;
using Aitex.Common.Util;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using EPInterface.Datas;
namespace VirgoRT.Modules
{
    /// 
    /// Recipe head
    /// 
    public class RecipeHead
    {
        public string RecipeVariation  { get; set; }
        public string CreationTime     { get; set; }
        public string LastRevisionTime { get; set; }
        public string CreatedBy        { get; set; }
        public string LastModifiedBy   { get; set; }
        public string PressureMode     { get; set; }
        public string Description      { get; set; }
        public string Barcode          { get; set; }
        public string BasePressure     { get; set; }
        public string PumpDownLimit    { get; set; }
        //public string ElectrodeTemp    { get; set; }
        //public string HeaterTemp       { get; set; }
        public string PurgeActive      { get; set; }
        public string MatchPositionC1  { get; set; }
        public string MatchPositionC2  { get; set; }
        public string SubstrateTemp    { get; set; }
        public string PumpingPinState  { get; set; }
        public string NotToPurgeOrVent { get; set; }    //For keep vacuum after idle clean
        public string VentingPinState  { get; set; }
        public string PinDownPressure { get; set; }
    }
    public class RecipeStep
    {
        public string StepName;
        public double StepTime;
        public bool IsJumpStep;
        public bool IsLoopStartStep;
        public bool IsLoopEndStep;
        public int LoopCount;
        public string EndBy;
        public string EndPointConfig;
        public bool FaultIfNoEPDTrigger;
        //public double EndByValue;
        public Dictionary RecipeCommands = new Dictionary();
    }
    public class Recipe
    {
        /// 
        /// 当前Recipe Run 记录对应的Guid
        /// 每个Recipe Run 都对应有一个唯一的Guid
        /// 
        public static Guid CurrentRecipeRunGuid;
        /// 
        /// 解析工艺程序文件
        /// 
        /// 工艺程序名
        /// xml格式的工艺数据内容
        /// 返回解析的工艺数据变量
        /// True:解析成功 | False:解析失败
        public static bool Parse(string chamberId, string recipe, out RecipeHead recipeHead, out List recipeData)
        {
            recipeHead = new RecipeHead();
            recipeData = new List();
            try
            {
                //获取工艺程序文件中允许的命令字符串列表
                //目的:如果工艺程序文件中含有规定之外的命令,则被禁止执行
                HashSet recipeAllowedCommands = new HashSet();
                XmlDocument rcpFormatDoc = new XmlDocument();
                string recipeSchema = PathManager.GetCfgDir() + "RecipeFormat.xml";
                rcpFormatDoc.Load(recipeSchema);
                XmlNodeList rcpItemNodeList = rcpFormatDoc.SelectNodes("/Aitex/TableRecipeFormat/Catalog/Group/Step");
                foreach (XmlElement item in rcpItemNodeList)
                    recipeAllowedCommands.Add(item.Attributes["ControlName"].Value);
                //获取工艺程序文件中所有步的内容
                XmlDocument rcpDataDoc = new XmlDocument();
                rcpDataDoc.LoadXml(recipe);
                recipeHead.PressureMode = rcpDataDoc.DocumentElement.HasAttribute("PressureMode") ? rcpDataDoc.DocumentElement.Attributes["PressureMode"].Value : "";
                recipeHead.BasePressure = rcpDataDoc.DocumentElement.HasAttribute("BasePressure") ? rcpDataDoc.DocumentElement.Attributes["BasePressure"].Value : "";
                recipeHead.PumpDownLimit = rcpDataDoc.DocumentElement.HasAttribute("PumpDownLimit") ? rcpDataDoc.DocumentElement.Attributes["PumpDownLimit"].Value : "";
                recipeHead.PurgeActive = rcpDataDoc.DocumentElement.HasAttribute("PurgeActive") ? rcpDataDoc.DocumentElement.Attributes["PurgeActive"].Value : "";
                recipeHead.MatchPositionC1 = rcpDataDoc.DocumentElement.HasAttribute("MatchPositionC1") ? rcpDataDoc.DocumentElement.Attributes["MatchPositionC1"].Value : "";
                recipeHead.MatchPositionC2 = rcpDataDoc.DocumentElement.HasAttribute("MatchPositionC2") ? rcpDataDoc.DocumentElement.Attributes["MatchPositionC2"].Value : "";
                recipeHead.Barcode = rcpDataDoc.DocumentElement.HasAttribute("Barcode") ? rcpDataDoc.DocumentElement.Attributes["Barcode"].Value : "";
                recipeHead.SubstrateTemp = rcpDataDoc.DocumentElement.HasAttribute("SubstrateTemp") ? rcpDataDoc.DocumentElement.Attributes["SubstrateTemp"].Value : "";
                recipeHead.PumpingPinState = rcpDataDoc.DocumentElement.HasAttribute("PumpingPinState") ? rcpDataDoc.DocumentElement.Attributes["PumpingPinState"].Value : "Down";
                
                //For keep vacuum after idle clean
                recipeHead.NotToPurgeOrVent = rcpDataDoc.DocumentElement.HasAttribute("NotToPurgeOrVent") ? rcpDataDoc.DocumentElement.Attributes["NotToPurgeOrVent"].Value : "";
                recipeHead.VentingPinState = rcpDataDoc.DocumentElement.HasAttribute("VentingPinState") ? rcpDataDoc.DocumentElement.Attributes["VentingPinState"].Value : "Down";
                recipeHead.PinDownPressure = rcpDataDoc.DocumentElement.HasAttribute("PinDownPressure") ? rcpDataDoc.DocumentElement.Attributes["PinDownPressure"].Value : "1000";
                XmlNodeList stepNodeList = rcpDataDoc.SelectNodes("/TableRecipeData/Step");
                for (int i = 0; i < stepNodeList.Count; i++)
                {
                    var recipeStep = new RecipeStep();
                    recipeData.Add(recipeStep);
                    XmlElement stepNode = stepNodeList[i] as XmlElement;
                    Dictionary dic = new Dictionary();
                    //遍历Step节点
                    foreach (XmlAttribute att in stepNode.Attributes)
                    {
                        if (recipeAllowedCommands.Contains(att.Name))
                        {
                            dic.Add(att.Name, att.Value);
                        }
                    }
                    //遍历Step子节点中所有的attribute属性节点
                    foreach (XmlElement subStepNode in stepNode.ChildNodes)
                    {
                        foreach (XmlAttribute att in subStepNode.Attributes)
                        {
                            if (recipeAllowedCommands.Contains(att.Name))
                            {
                                dic.Add(att.Name, att.Value);
                            }
                        }
                        //遍历Step子节点的子节点中所有的attribute属性节点
                        foreach (XmlElement subsubStepNode in subStepNode.ChildNodes)
                        {
                            foreach (XmlAttribute att in subsubStepNode.Attributes)
                            {
                                if (recipeAllowedCommands.Contains(att.Name))
                                {
                                    dic.Add(att.Name, att.Value);
                                }
                            }
                        }
                    }
                    recipeStep.IsJumpStep = true;//!Convert.ToBoolean(dic["Ramp"]);
                    recipeStep.StepName = dic["Name"];
                    recipeStep.StepTime = double.Parse(dic["Time"]);
                    string loopStr = dic["Loop"];
                    recipeStep.IsLoopStartStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop\x20\d+\s*$").Success;
                    recipeStep.IsLoopEndStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop End$").Success;
                    if (recipeStep.IsLoopStartStep)
                        recipeStep.LoopCount = Convert.ToInt32(loopStr.Replace("Loop", string.Empty));
                    else
                        recipeStep.LoopCount = 0;
                    //recipeStep.EndByValue = Convert.ToDouble(dic["EndValue"]);
                    recipeStep.EndBy = dic["EndBy"];
                    if (recipeStep.EndBy == "EndByRfTime")
                    {
                        recipeStep.StepTime = double.Parse(dic["Rf.SetPowerOnTime"]);
                        if (recipeStep.StepTime <= 0)
                        {
                            LOG.Error("The recipe does not define the time for RF Power on");
                            return false;
                        }
                    }
                    int rfPower = (int)Convert.ToDouble(dic["Rf.SetPower"]);
                    dic.Add("Rf.SetPowerOnOff", rfPower > 0 ? "true" : "false");
                    if (SC.GetValue($"{chamberId}.BiasRf.EnableBiasRF"))
                    {
                        int rfPowerBias = (int)Convert.ToDouble(dic["BiasRf.SetPower"]);
                        dic.Add("BiasRf.SetPowerOnOff", rfPowerBias > 0 ? "true" : "false");
                    }
                    else
                    {
                        dic.Remove("BiasRf.SetPower");
                        dic.Remove("BiasRf.SetMatchProcessMode");
                        dic.Remove("BiasRf.SetMatchPositionC1");
                        dic.Remove("BiasRf.SetMatchPositionC2");
                    }
                    bool epdInstalled = SC.ContainsItem("System.SetUp.EPDInstalled") && SC.GetValue($"System.SetUp.EPDInstalled");
                    if (!epdInstalled)
                    {
                        if (dic.ContainsKey("EPD.SetConfig"))
                            dic.Remove("EPD.SetConfig");
                    }
                    else
                    {
                        recipeStep.EndPointConfig = dic.ContainsKey("EPD.SetConfig") ? dic["EPD.SetConfig"] : null;
                        if (string.IsNullOrEmpty(recipeStep.EndPointConfig))
                        {
                            if (recipeStep.EndBy == "EndByEndPoint")
                            {
                                EV.PostWarningLog("System", "EndPoint is empty");
                                return false;
                            }
                            recipeStep.EndPointConfig = SC.GetStringValue("System.EndPoint.EndPointDefaultValue");
                        }
                        else
                        {
                            if (!ParseEPD(recipeStep.EndPointConfig, out EPDConfig config))
                            {
                                EV.PostWarningLog("System", "EndPoint config is not valid");
                                return false;
                            }
                            recipeStep.FaultIfNoEPDTrigger = config.FaultIfNoEPDTrigger;
                        }
                    }
                    //dic.Remove("Ramp");
                    dic.Remove("StepNo");
                    dic.Remove("Name");
                    dic.Remove("Loop");
                    dic.Remove("Time");
                    dic.Remove("EndBy");
                    //dic.Remove("EndValue");
                    dic.Remove("Rf.SetPowerOnTime");
                    foreach (string key in dic.Keys)
                        recipeStep.RecipeCommands.Add(key, dic[key]);
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                return false;
            }
            return true;
        }
        private static bool ParseEPD(string config, out EPDConfig epdConfig)
        {
            epdConfig = new EPDConfig();
            try
            {
                epdConfig.nParameterCount = 1;
                string[] items = config.Split(';');
                foreach (var item in items)
                {
                    if (string.IsNullOrEmpty(item))
                        continue;
                    string[] pairs = item.Split('=');
                    if (pairs.Length != 2)
                        continue;
                    switch (pairs[0])
                    {
                        case "ExposureTime":
                            epdConfig.Columns[0].nCCDExposureTime = int.Parse(pairs[1]);
                            break;
                        case "WaveLengthA":
                            epdConfig.Columns[0].nWaveLength[0] = ushort.Parse(pairs[1]);
                            break;
                        case "BinningA":
                            epdConfig.Columns[0].nBinning[0] = ushort.Parse(pairs[1]);
                            break;
                        case "WaveLengthB":
                            epdConfig.Columns[0].nWaveLength[1] = ushort.Parse(pairs[1]);
                            break;
                        case "BinningB":
                            epdConfig.Columns[0].nBinning[1] = ushort.Parse(pairs[1]);
                            break;
                        case "WaveLengthC":
                            epdConfig.Columns[0].nWaveLength[2] = ushort.Parse(pairs[1]);
                            break;
                        case "BinningC":
                            epdConfig.Columns[0].nBinning[2] = ushort.Parse(pairs[1]);
                            break;
                        case "WaveLengthD":
                            epdConfig.Columns[0].nWaveLength[3] = ushort.Parse(pairs[1]);
                            break;
                        case "BinningD":
                            epdConfig.Columns[0].nBinning[3] = ushort.Parse(pairs[1]);
                            break;
                        case "Fd":
                            epdConfig.Columns[0].cFunc = pairs[1];
                            break;
                        case "PrefilterTime":
                            epdConfig.Columns[0].nPreFilterTime = int.Parse(pairs[1]);
                            break;
                        case "PostfilterTime":
                            epdConfig.Columns[0].nPostFilterTime = int.Parse(pairs[1]);
                            break;
                        case "AlgorithmType":
                            epdConfig.Columns[0].algorithmType = MapType(pairs[1]);
                            break;
                        case "Criteria":
                            epdConfig.Columns[0].nCriteria = float.Parse(pairs[1]);
                            break;
                        case "DelayTime":
                            epdConfig.Columns[0].nDelayTime = int.Parse(pairs[1]);
                            break;
                        case "ValidationTime":
                            epdConfig.Columns[0].nValidationTime = int.Parse(pairs[1]);
                            break;
                        case "ValidationValue":
                            epdConfig.Columns[0].nValidationValue = int.Parse(pairs[1]);
                            break;
                        case "TimeWindow":
                            epdConfig.Columns[0].nTimeWindow = int.Parse(pairs[1]);
                            break;
                        case "MinimalTime":
                            epdConfig.Columns[0].nMinimalTime = int.Parse(pairs[1]);
                            break;
                        case "PostponeTime":
                            epdConfig.Columns[0].nPostponeTime = int.Parse(pairs[1]);
                            break;
                        case "Control":
                            epdConfig.Columns[0].bControl = Convert.ToBoolean(pairs[1]);
                            break;
                        case "Normalization":
                            epdConfig.Columns[0].bNormalization = Convert.ToBoolean(pairs[1]);
                            break;
                        case "EnablePostponePercent":
                            epdConfig.Columns[0].bPostponePercent = Convert.ToBoolean(pairs[1]);
                            break;
                        case "EnableCriterialPercent":
                            epdConfig.Columns[0].bCriteriaPercent = Convert.ToBoolean(pairs[1]);
                            break;
                        case "EnableEventTrigger":
                            epdConfig.Columns[0].bEvtTrigger = Convert.ToBoolean(pairs[1]);
                            break;
                        case "IsFaultIfNoTrigger":
                            epdConfig.FaultIfNoEPDTrigger = Convert.ToBoolean(pairs[1]);
                            break;
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                EV.PostMessage("System", EventEnum.DefaultAlarm, "EPD config input not valid, ", ex.Message);
                return false;
            }
             
        }
        private static AlgorithmType MapType(string type)
        {
            switch (type)
            {
                case "Unknown": return AlgorithmType.ALG_NONE;
                case "Above_ABS_Value": return AlgorithmType.ALG_RISE_VALUE;
                case "Below_ABS_Value": return AlgorithmType.ALG_FALL_VALUE;
                case "Drop_Percent": return AlgorithmType.ALG_FALL_PERCENT;
                case "Up_Percent": return AlgorithmType.ALG_RISE_PERCENT;
                case "Range_In": return AlgorithmType.ALG_RANGE_IN;
                case "Gradient": return AlgorithmType.ALG_GRADIENT;
                case "Peek": return AlgorithmType.ALG_PEAK;
                case "Valley": return AlgorithmType.ALG_VALLEY;
                case "Min_Drop_Percent": return AlgorithmType.ALG_MIN_FALL_PERCENT;
                case "Min_Up_Percent": return AlgorithmType.ALG_MIN_RISE_PERCENT;
                case "Max_Drop_Percent": return AlgorithmType.ALG_MAX_FALL_PERCENT;
                case "Max_Up_Percent": return AlgorithmType.ALG_MAX_RISE_PERCENT;
                case "Rise_Fall": return AlgorithmType.ALG_RISE_FALL;
                case "Fall_Rise": return AlgorithmType.ALG_FALL_RISE;
            }
            return AlgorithmType.ALG_NONE;
        }
    }
}