using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Venus_Core.Attributes;

namespace Venus_Core
{
    public enum RecipeType
    {
        Chuck,
        DeChuck,
        Process,
        Clean,
    }

    public enum StepType
    {
        Time,
        Stable,
        EndPoint,
        OverEtch,
    }

    public enum ProcessModule
    {
        Pressure,
        TCP,            // Transformer Coupled Plasma (转换耦合等离子)
        Bias,
        Gas,
        ESC,
        Process,
        Compound,
    }
    public enum GeneratorMode
    {
        Pulsing,
        CW,
    }

    public enum PressureUnitMode
    {
        Pressure,
        Valve
    }

    public class RecipeHead: INotifyPropertyChanged
    {
        private string m_name;
        [IsOnlyRead]
        public string Name
        {
            get { return m_name; }
            set { m_name = value; InvokePropertyChanged("Name"); }
        }

        private string _Version = "TestVersion";
        public string Version
        {
            get { return _Version; }
            set { _Version = value; InvokePropertyChanged("Version"); }
        }

        private JetChamber _ChamberType = JetChamber.Venus;
        public JetChamber ChamberType
        {
            get { return _ChamberType; }
            set { _ChamberType = value; InvokePropertyChanged("ChamberType"); }
        }

        private RecipeType m_Type;
        [JsonConverter(typeof(StringEnumConverter))]
        public RecipeType Type
        {
            get { return m_Type; }
            set { m_Type = value; InvokePropertyChanged("Type"); }
        }
        private string m_ChunckRecipe;
        public string ChuckRecipe
        {
            get { return m_ChunckRecipe;}
            set { m_ChunckRecipe = value; InvokePropertyChanged("ChuckRecipe"); }
        }
        private string m_DechuckRecipe;
        public string DechuckRecipe
        {
            get { return m_DechuckRecipe;}
            set{ m_DechuckRecipe = value;InvokePropertyChanged("DechuckRecipe");}
        }
        private string m_CreateTime;
        [IsOnlyRead]
        public string CreateTime
        {
            get { return m_CreateTime;}
            set { m_CreateTime = value; InvokePropertyChanged("CreateTime"); }
        }
        private string m_EditTime;
        [IsOnlyRead]
        public string EditTime
        {
            get { return m_EditTime; }
            set { m_EditTime = value; InvokePropertyChanged("EditTime"); }
        }
        private string m_LastModifiedBy;
        [IsOnlyRead]
        public string LastModifiedBy
        {
            get {  return m_LastModifiedBy;}
            set { m_LastModifiedBy = value; InvokePropertyChanged("LastModifiedBy"); }
        }
        private string m_Barcode;
        public string Barcode
        {
            get { return m_Barcode;}
            set { m_Barcode = value; InvokePropertyChanged("Barcode"); }
        }
        private string m_BasePressure;
        public string BasePressure
        {
            get { return m_BasePressure;}
            set { m_BasePressure = value; InvokePropertyChanged("BasePressure"); }
        }
        private string m_ChillerTemp;
        public string ChillerTemp
        {
            get { return m_ChillerTemp;}
            set { m_ChillerTemp = value; InvokePropertyChanged("ChillerTemp"); }
        }
        private int m_RFHoldTime=1000;
        public int RFHoldTime
        {
            get { return m_RFHoldTime; }
            set { m_RFHoldTime = value; InvokePropertyChanged("RFHoldTime"); }
        }
        private int m_BiasRFHoldTime = 1000;
        public int BiasRFHoldTime
        {
            get { return m_BiasRFHoldTime; }
            set { m_BiasRFHoldTime = value; InvokePropertyChanged("BiasRFHoldTime"); }
        }
        private string m_Comment;
        public string Comment
        {
            get { return m_Comment; }
            set { m_Comment = value; InvokePropertyChanged("Comment"); }
        }
        //private string m_ChillerTemp1;
        //public string ChillerTemp1
        //{
        //    get { return m_ChillerTemp1; }
        //    set { m_ChillerTemp1 = value; InvokePropertyChanged("ChillerTemp1"); }
        //}
        //private string m_ChillerTemp2;
        //public string ChillerTemp2
        //{
        //    get { return m_ChillerTemp2; }
        //    set { m_ChillerTemp2 = value; InvokePropertyChanged("ChillerTemp2"); }
        //}

        //private string m_ChillerTemp3;
        //public string ChillerTemp3
        //{
        //    get { return m_ChillerTemp3; }
        //    set { m_ChillerTemp3 = value; InvokePropertyChanged("ChillerTemp3"); }
        //}

        //private string m_ChillerTemp4;
        //public string ChillerTemp4
        //{
        //    get { return m_ChillerTemp4; }
        //    set { m_ChillerTemp4 = value; InvokePropertyChanged("ChillerTemp4"); }
        //}

        public event PropertyChangedEventHandler PropertyChanged;

        public void InvokePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class ProcessUnitBase
    {
        [JsonIgnore]
        public Func<ProcessUnitBase, RecipeStep, RState> checker;
        [JsonIgnore]
        public Func<ProcessUnitBase, RecipeStep, RState> starter;
        [JsonIgnore]
        public Action<ProcessUnitBase, RecipeStep> end;
        //[JsonIgnore]
        //public string Name { get; set; }
        //[JsonIgnore]
        //public ProcessModule Moudle { get; set; }

        public RState Start(RecipeStep step)
        {
            if (starter != null)
                return starter(this, step);

            return RState.Running;
        }
        public RState Run(RecipeStep step)
        {
            if (checker != null)
                return checker(this, step);

            return RState.Running;
        }

        public void End(RecipeStep step)
        {
            if (end != null)
                end(this, step);
        }
    }
    [Serializable]
    public class RecipeStep:INotifyPropertyChanged
    {

        [JsonIgnore]
        public Func<RecipeStep, RState> checker;
        [JsonIgnore]
        public Func<RecipeStep, RState> starter;
        [JsonIgnore]
        public Func<RecipeStep, RState> ender;


        private int m_StepNo;
        [IsOnlyRead]
        public int StepNo
        {
            get { return m_StepNo; }
            set { m_StepNo = value; InvokePropertyChanged("StepNo"); }
        }
        private StepType m_StepType;
        [JsonConverter(typeof(StringEnumConverter))]
        public StepType Type
        {
            get { return m_StepType;}
            set { m_StepType = value; InvokePropertyChanged("Type"); }
        }

        private int m_Time;
        public int Time
        {
            get { return m_Time;}
            set { m_Time = value; InvokePropertyChanged("Time"); }
        }
        private string m_Description;
        public string Description
        {
            get { return m_Description; }
            set { m_Description = value; InvokePropertyChanged("Description"); }
        }
        private string m_EPDConfigName;
        public string EPDConfig
        {
            get { return m_EPDConfigName; }
            set { m_EPDConfigName = value; InvokePropertyChanged("EPDConfig"); }
        }
        private int m_MinEndPointTime;
        public int MinEndPointTime
        {
            get { return m_MinEndPointTime; }
            set { m_MinEndPointTime = value; InvokePropertyChanged("MinEndPointTime"); }
        }

        private int m_MaxEndPointTime;
        public int MaxEndPointTime
        {
            get { return m_MaxEndPointTime; }
            set { m_MaxEndPointTime = value; InvokePropertyChanged("MaxEndPointTime"); }
        }
        //private bool m_EnableRamp;
        //public bool EnableRamp
        //{
        //    get { return m_EnableRamp; }
        //    set { m_EnableRamp = value; InvokePropertyChanged("EnableRamp"); }
        //}





        private int m_OverEtchPercent;
        public int OverEtchPercent
        {
            get { return m_OverEtchPercent; }
            set { m_OverEtchPercent = value; InvokePropertyChanged("OverEtchPercent"); }
        }

        private bool m_CycleStart;
        public bool CycleStart
        {
            get { return m_CycleStart;}
            set { m_CycleStart = value; InvokePropertyChanged("CycleStart"); }
        }
        private bool m_CycleEnd;
        public bool CycleEnd
        {
            get { return m_CycleEnd;}
            set { m_CycleEnd = value; InvokePropertyChanged("CycleEnd"); }
        }
        private int m_CycleNumber;
        public int CycleNumber
        {
            get { return m_CycleNumber;}
            set { m_CycleNumber = value; InvokePropertyChanged("CycleNumber"); }
        }

        private ObservableCollection<Object> lstUnit = new ObservableCollection<Object>();
        public ObservableCollection<Object> LstUnit
        {
            get { return lstUnit; }
            set { lstUnit = value; InvokePropertyChanged("LstUnit"); }
        }

        private Stopwatch _stepTimer = new Stopwatch();

        public long ElapsedTime()
        {
            return _stepTimer.ElapsedMilliseconds; 
        }
        public void StartStepTimer()
        {
            _stepTimer.Restart();
        }

        private long _lastEPDStepTimne = 0;
        public void RecordEPDStepTime()
        {
            _lastEPDStepTimne = _stepTimer.ElapsedMilliseconds;
        }

        public long GetLastEPDStepTime()
        {
            return _lastEPDStepTimne;
        }

        public RState Start()
        {
            starter(this);

            foreach (var unit in lstUnit)
            {
                var processUnit = unit as ProcessUnitBase;
                if (processUnit != null)
                {
                    var state = processUnit.Start(this);
                    if (state != RState.Running)
                        return state;
                }
                else
                    return RState.Failed;
                
            }

            return RState.Running;
        }
        public RState Run()
        {
            if (checker(this) == RState.End)
                return RState.End;

            bool bStable = true;
            foreach (var unit in lstUnit)
            {
                var processUnit = unit as ProcessUnitBase;
                if (processUnit != null)
                {
                    var state = processUnit.Run(this);
                    if(Type == StepType.Stable)
                    {
                        if (state != RState.Running && state != RState.End)
                            return state;

                        if (state == RState.Running)
                            bStable = false;
                    }
                    else
                    {
                        if (state != RState.Running)
                            return state;
                    }
                }
                else
                    return RState.Failed;
            }

            return (Type == StepType.Stable && bStable) ? RState.End : RState.Running;
        }

        public void End()
        {
            foreach (var unit in lstUnit)
            {
                var processUnit = unit as ProcessUnitBase;
                if (processUnit != null)
                {
                    processUnit.End(this);
                }
            }

            ender(this);
        }

        public double RampFactor()
        {
            return _stepTimer.ElapsedMilliseconds / (Time * 1000.0);
        }

        public void AppendUnit(ProcessUnitBase unit)
        {
            lstUnit.Append(unit);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void InvokePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }


    public class Recipe: INotifyPropertyChanged
    {

        private RecipeHead m_Header = new RecipeHead();
        public RecipeHead Header
        {
            get { return m_Header; }
            set { m_Header = value; InvokePropertyChanged("Header"); }
        }

        private ObservableCollection<RecipeStep> m_Steps;
        public ObservableCollection<RecipeStep> Steps
        {
            get { return m_Steps; }
            set { m_Steps = value; InvokePropertyChanged("Steps"); }
        }

        public static Recipe Load(string recipeFile)
        {
           var recipe= JsonConvert.DeserializeObject<Recipe>(recipeFile);
            if (recipe == null)
            {
                return null;
            }
            foreach (var step in recipe.Steps)
            {
                ObservableCollection<ProcessUnitBase> unit = new ObservableCollection<ProcessUnitBase>();
                //int count = step.LstUnit.Count / 2;
                //for (int i = 0; i < count; i++)
                //{
                //    Type t = Type.GetType(step.LstUnit[i].ToString());
                //    //unit.Add(JsonConvert.DeserializeObject<t>(step.LstUnit[i+count].ToString()));
                //    unit.Add(JsonConvert.DeserializeObject<ProcessUnitBase>(step.LstUnit[i + count].ToString()));

                //}
                //var item = step.LstUnit[0];

                for (int i=0;i< step.LstUnit.Count; i++)
                {
                    //object item=step.LstUnit[i];
                    //step.LstUnit[i].
                    string value = step.LstUnit[i].ToString();
                    string[] striparr = value.Split(new string[] { "\r\n" }, StringSplitOptions.None);
                    string item1= striparr[1].Remove(0, 15);
                    string item2 = item1.Remove(item1.Length - 2, 2);
                    //var item= value.Substring(value.IndexOf("UnitName"));

                    //Type t = Type.GetType(value);
                    

                    switch (item2)
                    {
                        case "PressureModeUnit":
                            unit.Add(JsonConvert.DeserializeObject<PressureByPressureModeUnit>(step.LstUnit[i].ToString()));
                            break;

                        //case "PressureByValveModeUnit":
                        //    unit.Add(JsonConvert.DeserializeObject<PressureByValveModeUnit>(step.LstUnit[i].ToString()));
                        //    break;
                        case "TCPUnit":
                            unit.Add(JsonConvert.DeserializeObject<TCPUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "BiasUnit":
                            unit.Add(JsonConvert.DeserializeObject<BiasUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "GasControlUnit":
                            unit.Add(JsonConvert.DeserializeObject<GasControlUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "ESCHVUnit":
                            unit.Add(JsonConvert.DeserializeObject<ESCHVUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "ProcessKitUnit":
                            unit.Add(JsonConvert.DeserializeObject<ProcessKitUnit>(step.LstUnit[i].ToString()));
                            break;

                        case "HeaterUnit":
                            unit.Add(JsonConvert.DeserializeObject<HeaterUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "GasUnit":
                            unit.Add(JsonConvert.DeserializeObject<Kepler2200GasControlUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "RFUnit":
                            unit.Add(JsonConvert.DeserializeObject<Kepler2200RFUnit>(step.LstUnit[i].ToString()));
                            break;

                            
                    }
                }
                step.LstUnit.Clear();
                unit.ToList().ForEach(x => 
                {
                    step.LstUnit.Add(x);
                });
               
                
            }
            return recipe;
        }

        public bool Validate()
        {
            return true;
        }

        public bool Save(string recipeFile)
        {
            return true;
        }


        public event PropertyChangedEventHandler PropertyChanged;

        public void InvokePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }



    }
    public class RecipeUnity
    {
        public static string ConvertJsonString(string str)
        {
            JsonSerializer serializer = new JsonSerializer();
            TextReader tr = new StringReader(str);
            JsonTextReader jtr = new JsonTextReader(tr);
            object obj = serializer.Deserialize(jtr);
            if (obj != null)
            {
                StringWriter textWriter = new StringWriter();
                JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
                {
                    Formatting = Newtonsoft.Json.Formatting.Indented,
                    Indentation = 4,
                    IndentChar = ' '
                };
                serializer.Serialize(jsonWriter, obj);
                return textWriter.ToString();
            }
            else
            {
                return str;
            }
        }
        public static string RecipeToString(Recipe recipe)
        {
            return JsonConvert.SerializeObject(recipe);
        }
        public static String CreateRecipe(JetChamber currentChamber, RecipeType recipeType,string recipeName)
        {
            Recipe recipe = new Recipe();
            recipe.Header = new RecipeHead();
            recipe.Header.CreateTime = DateTime.Now.ToString();
            recipe.Header.EditTime = DateTime.Now.ToString();
            recipe.Header.Type = recipeType;
            recipe.Header.ChamberType= currentChamber;
            recipe.Header.Name = recipeName;
            recipe.Header.LastModifiedBy = "Admin";
            recipe.Steps = new ObservableCollection<RecipeStep>();

            switch (currentChamber)
            {
                case JetChamber.Venus:
                    recipe.Steps.Add(new RecipeStep()
                    {
                        StepNo = 1,
                        LstUnit = new ObservableCollection<Object>()
                {
                 new PressureByPressureModeUnit(),
                 new TCPUnit(),
                 new BiasUnit(),
                 new GasControlUnit(),
                 new ESCHVUnit(),
                 new ProcessKitUnit()
                 }
                    });
                    break;
                case JetChamber.Kepler2300:
                    recipe.Steps.Add(new RecipeStep()
                    {
                        StepNo = 1,
                        LstUnit = new ObservableCollection<Object>()
                {
                 new PressureByPressureModeUnit(),
                 new TCPUnit(),
                 new BiasUnit(),
                 new GasControlUnit(),
                 new ProcessKitUnit()
                 }
                    });
                    break;

                case JetChamber.Kepler2200A:
                    recipe.Steps.Add(new RecipeStep()
                    {
                        StepNo = 1,


                        LstUnit = new ObservableCollection<Object>()
                {
             new Kepler2200GasControlUnit(),
             new HeaterUnit(),
             new Kepler2200RFUnit()
                 }
                    });

                    break;
            }

          
            var recipeString = JsonConvert.SerializeObject(recipe);
            //var Recipe2=JsonConvert.DeserializeObject<Recipe>(recipeString);
            //string test = "";
            //test = Recipe2.Steps[0].LstUnit[0].ToString();
            //var tcpinit = JsonConvert.DeserializeObject<TCPUnit>(test);
            return recipeString;
        }
    }
}