using System.Collections.Generic;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.SCCore;
using Venus_RT.Devices;
using Venus_RT.Devices.IODevices;
using Aitex.Core.RT.Log;
using Venus_Core;
using Aitex.Core.Util;
using System;
using MECF.Framework.Common.DBCore;
using MECF.Framework.Common.Routine;
using System.Collections.ObjectModel;

namespace Venus_RT.Modules.PMs
{
    class PMGasVerificationRoutine : PMRoutineBase, IRoutine
    {
        private enum GasStep
        {
            kPrepareValves,
            kPumpDown_1,
            kGasFlow,
            KDelay_2S,
            KGetBeginPressure,
            kDelay_1,
            kGasVerification,
            kStopGasFlow,
            kPumpDown_2,
            kEnd,
        }
        public enum VerifyMode
        {
            OnePoint,
            TenPoint,
        }

        private double _basePressure;
        private double _beginPressure;
        private double _endPressure;
        private double _elapsedTime;
        private DeviceTimer _verificationDeviceTimer = new DeviceTimer();

        private int _mfcIndex;
        private float _mfcFlow;
        private int _flowTime;
        private int _pumpingTime;
        private int _holdTime;
        private double _mfcActualFlow;
        private double _maxPressure;

        private MfcBase1 _mfcDevice;
        private VerifyMode _paramMode;
        private Dictionary<int, float> _paramFlowSet = new Dictionary<int, float>();
        private Dictionary<float, Tuple<float, float>> _calibrationResult = new Dictionary<float, Tuple<float, float>>();
        private float _pressureStableTolerance = 2;//2mTorr
        private float _flowStableTolerance = 0.02f;//2%
        private double _chamberVolume;
        private double _gasTemperature;
        private double _leakRate;

        private double _maxDeviation;

        private readonly PumpDownRoutine _pumpDownRoutine;
        public ObservableCollection<MFCCalibrationData> _MFCCalibrationDatas = new ObservableCollection<MFCCalibrationData>();

        public PMGasVerificationRoutine(JetPMBase chamber,PumpDownRoutine pdRoutine) : base(chamber)
        {
            Name = "Gas Verification";
            _pumpDownRoutine = pdRoutine;
        }

        internal void Init(string mfc, double flow, int flowCount)
        {
            int.TryParse(mfc.Replace("MFC", ""), out _mfcIndex);

            _mfcDevice = DEVICE.GetDevice<MfcBase1>($"{Module}.MfcGas{_mfcIndex}");
            _mfcIndex -= 1;//start from 0
            _mfcFlow = (float)flow;

            if (flowCount == 10)
                _paramMode = VerifyMode.TenPoint;
            else
                _paramMode = VerifyMode.OnePoint;
        }
        public RState Start(params object[] objs)
        {
            if (!CheckLid())
            {
                return RState.Failed;
            }

            if (!CheckSlitDoor())
            {
                return RState.Failed;
            }

            if (!CheckDryPump())
            {
                return RState.Failed;
            }

            if(!CheckTurboPump())
            {
                return RState.Failed;
            }

            _calibrationResult.Clear();
            _paramFlowSet.Clear();
            _MFCCalibrationDatas.Clear();

            _basePressure = SC.GetValue<double>($"{Module}.MFCVerification.BasePressure");

            _chamberVolume = SC.GetValue<double>($"{Module}.MFCVerification.ChamberVolume");

            _gasTemperature = SC.GetValue<double>($"{Module}.MFCVerification.GasTemperature");

            _pumpingTime = SC.GetValue<int>($"{Module}.MFCVerification.PumpingTime");
            _flowTime = SC.GetValue<int>($"{Module}.MFCVerification.GasFlowTime");
            _holdTime = SC.GetValue<int>($"{Module}.MFCVerification.HoldTime");

            _maxDeviation = SC.GetValue<double>($"{Module}.MFCVerification.MaxDeviation");

            _pressureStableTolerance = (float)SC.GetValue<double>($"{Module}.MFCVerification.PressureStableTolerance");

            _flowStableTolerance = (float)(SC.GetValue<double>($"{Module}.MFCVerification.FlowStableTolerance") / 100.0);

            _maxPressure = SC.GetValue<double>($"{Module}.MFCVerification.TargetPressure");

            if (_paramMode == VerifyMode.TenPoint)
            {
                for (int i = 0; i < 10; i++)
                {
                    _paramFlowSet.Add(i, (float)_mfcDevice.Scale * (i + 1) / 10);
                }
            }
            else
            {
                if (_mfcFlow <= 0 || _mfcFlow > _mfcDevice.Scale)
                {
                    LOG.Write(eEvent.ERR_ROUTINE_FAILED, Module, Name, $"MFC set value {_mfcFlow} not valid");
                    return RState.Failed;
                }
                _paramFlowSet.Add(0, _mfcFlow);
            }

            _mfcDevice.ResetVerificationData();
            _leakRate = 0;
            var dbData = DataQuery.Query($"SELECT * FROM \"leak_check_data\" where \"module_name\" = '{Module}' order by \"operate_time\" DESC;");
            if (dbData != null && dbData.Rows.Count > 0 && !dbData.Rows[0]["leak_rate"].Equals(DBNull.Value))
            {
                _leakRate = Convert.ToDouble(dbData.Rows[0]["leak_rate"]);
            }

            Reset();

            return Runner.Start(Module, Name);
        }
        public RState Monitor()
        {
            Runner.Run((int)GasStep.kPrepareValves,     PrepareValve,       _delay_50ms)
                .LoopStart((int)GasStep.kPumpDown_1, $"{_mfcDevice.Name} Gas Verification", _paramFlowSet.Count, PumpingDown, WaitPumpDone)
                .LoopRun((int)GasStep.kGasFlow, FlowGas, CheckGasStable)
                .LoopDelay((int)GasStep.KDelay_2S, 2 * 1000)
                .LoopRun((int)GasStep.KGetBeginPressure, GetBeginPressure)
                .LoopDelay((int)GasStep.kDelay_1, _holdTime * 1000)
                .LoopRun((int)GasStep.kGasVerification, CalcMfcCalibration, _delay_2s)
                .LoopEnd((int)GasStep.kStopGasFlow,     StopGasFlow,    IsPumpDownOK)
                .End((int)GasStep.kEnd,                 NullFun,            _delay_2s);

            return Runner.Status;
        }
        public void Abort()
        {
            _verificationDeviceTimer.Stop();
            _chamber.StopAllGases();
            _chamber.CloseValves();
            _mfcDevice.ResetVerificationData();
        }

        private bool PrepareValve()
        {
            _chamber.CloseValves();
            _chamber.OpenValve(ValveType.TurboPumpPumping, true);
            _chamber.OpenValve(ValveType.Guage, true);
            _chamber.OpenValve(ValveType.GasFinal, true);
            return true;
        }

        private bool PumpingDown()
        {
            _chamber.SetPVPostion(1000);
            return true;
        }

        private bool WaitPumpDone()
        {
            if(Runner.StepElapsedMS >= _pumpingTime * 1000)
            {
                if(_chamber.ProcessPressure <= _basePressure)
                {
                    return true;
                }
                else
                {
                    Runner.Stop($"MFC Gas Verification fail, Cannot pumping down to {_basePressure} mTorr in {_pumpingTime} seconds");
                    return true;
                }
            }

            return false;
        }

        private void OpenPVNVlv(int mfcIndex, bool on)
        {
            ValveType[] vlvs = new ValveType[] { ValveType.PV11, ValveType.PV21, ValveType.PV31, ValveType.PV41 };
            if(mfcIndex < 4)
            {
                _chamber.OpenValve(vlvs[mfcIndex], on);
            }
        }

        private bool FlowGas()
        {
            Notify($"Start {_mfcDevice.Name} gas flow");
            _verificationDeviceTimer.Start(0);
            _beginPressure = _chamber.ChamberPressure;

            OpenPVNVlv(_mfcIndex, true);
            if (!_chamber.FlowGas(_mfcIndex, _paramMode == VerifyMode.TenPoint ? _paramFlowSet[Runner.LoopCounter] : _mfcFlow))
            {
                return false;
            }

            return true;
        }

        private bool CheckGasStable()
        {
            if (_verificationDeviceTimer.GetElapseTime() > _flowTime * 1000)
            {
                
                //System.Threading.Thread.Sleep(1000);
                //if (!_chamber.SetPVPostion(0))
                    if(!_chamber.TurnPendulumValve(false))
                    Runner.Stop("Stop Pendulum Valve failed.");

                return true;
            }


            //if (Math.Abs(_chamber.ChamberPressure - _beginPressure) > _pressureStableTolerance)
            //{
            //    Runner.Stop($"");
            //    return true;
            //}
            if (_verificationDeviceTimer.GetElapseTime() > 3000)
            {
                if (Math.Abs(_mfcDevice.SetPoint - _mfcDevice.FeedBack) / _mfcDevice.SetPoint > _flowStableTolerance)
                {
                    Runner.Stop($"Gas is not Stable");
                    return true;
                }
            }
           
  
            return false;
        }

        private bool GetBeginPressure()
        {
            Notify($"Get begin pressure {_chamber.ChamberPressure.ToString("f1")}");
            _beginPressure = _chamber.ChamberPressure;
            _verificationDeviceTimer.Start(0);

            //Notify($"Check finished one point");
            return true;
        }

        private bool CalcMfcCalibration()
        {
            //_mfcIndex += 1;
            // full open Pendulum valve
            _chamber.SetPVPostion(1000);

            _endPressure = _chamber.ChamberPressure; //mTorr
            _elapsedTime = _verificationDeviceTimer.GetElapseTime() / (1000 * 60); //unit minutes

            float flow = _paramMode == VerifyMode.TenPoint ? _paramFlowSet[Runner.LoopCounter] : _mfcFlow;
            _mfcActualFlow = 273.15 * _chamberVolume / ((273.15 + _gasTemperature) * 760000) * ((_endPressure - _beginPressure) / _elapsedTime - _leakRate);
            Notify($"Calculate flow: calculate flow={_mfcActualFlow}, setpoint={flow}, begin pressure(mtorr)={_beginPressure:f3}, end pressure(mtorr)={_endPressure:f3}," +
                $"elapsed time(minute)={_elapsedTime:f3}");
            _MFCCalibrationDatas.Add(new MFCCalibrationData(flow,_mfcActualFlow));
            double deviation = (Math.Abs(_mfcActualFlow) - Math.Abs(flow)) / Math.Abs(flow) * 100;
            bool isOk = Math.Abs(deviation) <= Math.Abs(_maxDeviation);

            if (!isOk)
            {
                LOG.Write(eEvent.ERR_ROUTINE_FAILED, Module, Name, $"MFC{(_mfcIndex+1).ToString()} verify failed, deviation{deviation} exceed max tolerance{_maxDeviation}");
            }

            if (_paramMode == VerifyMode.TenPoint)
            {
                _calibrationResult[flow] = Tuple.Create((float)_mfcActualFlow, (float)_elapsedTime);

                _mfcDevice.SetVerificationResult((float)flow, (float)_mfcActualFlow, _calibrationResult.Count == 10, _elapsedTime * 60, deviation, isOk,(int)VerifyMode.TenPoint);
            }
            else if(_paramMode == VerifyMode.OnePoint)
            {
                _mfcDevice.SetVerificationResult((float)flow, (float)_mfcActualFlow, true, _elapsedTime * 60, deviation, isOk, (int)VerifyMode.OnePoint);
            }

            return true;
        }

        private bool StopGasFlow()
        {
            Notify($"Stop gas {_mfcIndex} flow");

            if (!_chamber.FlowGas(_mfcIndex, 0))
            {
                return false;
            }

            OpenPVNVlv(_mfcIndex, false);

            _chamber.OpenValve(ValveType.TurboPumpPumping, false);
            _chamber.OpenValve(ValveType.FastPump, true);


           return _pumpDownRoutine.Start(_basePressure) == RState.Running;
        }

        private bool IsPumpDownOK()
        {
            var status = _pumpDownRoutine.Monitor();
            if (status == RState.End)
            {
                return true;
            }
            else if (status == RState.Failed || status == RState.Timeout)
            {
                Runner.Stop($"Pump down to {_basePressure} failed.");
                return true;
            }

            return false;
        }
    }
}