using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
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
    {
        CW,
        Pulsing,
    }

    public enum PressureUnitMode
    {
        Pressure,
        Valve
    }
    public enum Suspect
    {
        Origin,
        Position1,
        Position2,
        Position3,
        Position4,
        Position5
    }
    public enum TargetMode
    {
        Step,
        Cycle
    }
    public enum ToleranceMode
    {
        None,
        Value,
        Percent
    }
    public enum MatchWorkMode
    {
        Auto,
        Manual
    }
    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;
        [JsonConverter(typeof(StringEnumConverter))]
        [IsOnlyRead]
        public JetChamber ChamberType
        {
            get { return _ChamberType; }
            set { _ChamberType = value; InvokePropertyChanged("ChamberType"); }
        }

        private RecipeType m_Type;
        [JsonConverter(typeof(StringEnumConverter))]
        [IsOnlyRead]
        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 bool m_IsShowTolerance;
        public bool IsShowTolerance
        {
            get { return m_IsShowTolerance; }
            set { m_IsShowTolerance = value; InvokePropertyChanged("IsShowTolerance"); }
        }
        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_Temperature;
        public string Temperature
        {
            get { return m_Temperature; }
            set { m_Temperature = value; InvokePropertyChanged("Temperature"); }
        }
        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 = 1;
        [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 Start(int steps, int currentstep)
        {
            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>();

                for (int i = 0; i < step.LstUnit.Count; 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);

                    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 "RFBoxUnit":
                            unit.Add(JsonConvert.DeserializeObject<RFBoxUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "GasUnit":
                            unit.Add(JsonConvert.DeserializeObject<Kepler2200GasControlUnit>(step.LstUnit[i].ToString()));
                            break;
                        case "SEGasControlUnit":
                            unit.Add(JsonConvert.DeserializeObject<VenusSEGasControlUnit>(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>();
            RecipeStep recipeStep = new RecipeStep();
            recipeStep.LstUnit = GetAllUnits(currentChamber, recipeType);
            recipe.Steps.Add(recipeStep);

            var recipeString = JsonConvert.SerializeObject(recipe);

            return recipeString;
        }

        public static ObservableCollection<Object> GetAllUnits(JetChamber jetChamber, RecipeType recipeType)
        {
            ObservableCollection<Object> LstUnit = new ObservableCollection<object>();
            switch (jetChamber)
            {
                case JetChamber.Venus:
                    if (recipeType == RecipeType.Clean)
                    {
                        foreach (var item in Enum.GetValues(typeof(VenusCleanRecipeUnits)))
                        {
                            Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                            var obj = System.Activator.CreateInstance(t);
                            LstUnit.Add(obj);
                        }
                    }
                    else
                    {
                        foreach (var item in Enum.GetValues(typeof(VenusUnits)))
                        {
                            Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                            var obj = System.Activator.CreateInstance(t);
                            LstUnit.Add(obj);
                        }
                    }

                    break;

                case JetChamber.Kepler2300:
                    if (recipeType == RecipeType.Clean)
                    {
                        foreach (var item in Enum.GetValues(typeof(Kepler2300CleanRecipeUints)))
                        {
                            Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                            var obj = System.Activator.CreateInstance(t);
                            LstUnit.Add(obj);
                        }
                    }
                    else
                    {
                        foreach (var item in Enum.GetValues(typeof(Kepler2300Uints)))
                        {
                            Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                            var obj = System.Activator.CreateInstance(t);
                            LstUnit.Add(obj);
                        }
                    }
                    break;
                case JetChamber.Kepler2200A:
                    foreach (var item in Enum.GetValues(typeof(Kepler2200AUnits)))
                    {
                        Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                        var obj = System.Activator.CreateInstance(t);
                        LstUnit.Add(obj);
                    }
                    break;
                case JetChamber.Kepler2200B:
                    foreach (var item in Enum.GetValues(typeof(Kepler2200BUnits)))
                    {
                        Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                        var obj = System.Activator.CreateInstance(t);
                        LstUnit.Add(obj);
                    }
                    break;
                case JetChamber.VenusSE:
                    foreach (var item in Enum.GetValues(typeof(VenusSEUnits)))
                    {
                        Type t = Type.GetType($"Venus_Core.{item.ToString()}");
                        var obj = System.Activator.CreateInstance(t);
                        LstUnit.Add(obj);
                    }
                    break;
            }
            return LstUnit;
        }
    }
}