using System;
using Aitex.Core.Common;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device.Devices;
using Aitex.Core.RT.Device.Unit;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using Aitex.Sorter.Common;
using MECF.Framework.Common.Communications;
using MECF.Framework.Common.SubstrateTrackings;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadPorts.LoadPortBase;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadPorts.TDK;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robots.RobotBase;



namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadPorts.OpenStages
{
    public class OpenStageHHGyy : LoadPortBaseDevice, IConnection
    {
        public EnumLoadPortType PortType { get; set; }
        public bool Initalized { get; set; }
        public bool Error { get; set; }
        public override string InfoPadCarrierType { get; set; } = "";
        public override FoupDoorState DoorState
        {
            get
            {
                return FoupDoorState.Open;
            }
        }
        private IoSensor[] _diInfoPadA;
        private IoSensor[] _diInfoPadB;
        private IoSensor[] _diInfoPadC;
        private IoSensor _diPresence;
        private IoSensor _diWaferProtrude;

        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorCstPresent;
        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorCstPlacement;
        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorCstA;
        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorCstB;
        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorCstC;
        private Aitex.Core.RT.Device.Unit.IoTrigger _doIndicatorAlarm;




        private int m_InfoPadIndex;
        private int m_IndicatorPadIndex;
        private RD_TRIG _trigPresentAbsent = new RD_TRIG();
        private RD_TRIG _trigPresentAbsentDely = new RD_TRIG();

        private RD_TRIG _trigPlacement = new RD_TRIG();

        private R_TRIG _trigWaferProtrude = new R_TRIG();
        private R_TRIG _trigCoverClosed = new R_TRIG();

        private R_TRIG _trigDoorOpen = new R_TRIG();
        private R_TRIG _trigDoorClose = new R_TRIG();

        DeviceTimer _deviceTimer = new DeviceTimer();
        private int _queryPeriod = 0;    //ms
        //private bool _isStillThere = false;

        private bool _isVirtualMode = false;
        private bool _isLoaded = false;
        private string _carrierInformation = "";

        public override LoadportCassetteState CassetteState
        {
            get
            {
                if (!_diPresence.Value)
                {
                    return LoadportCassetteState.Absent;
                }
                if (_diPresence.Value && (m_IndicatorPadIndex == 60 || m_IndicatorPadIndex == 48 || m_IndicatorPadIndex == 15))
                    return LoadportCassetteState.Normal;
                return LoadportCassetteState.Unknown;

            }
        }

        public string Address { get => ""; }

        public bool IsConnected => true;

        public void SetPrensentAbsentDelay(int ms)
        {
            _queryPeriod = ms;
        }

        public OpenStageHHGyy(string module, string name, IoSensor[] dis, IoTrigger[] indicators, RobotBaseDevice mapRobot) : base(
            module, name, mapRobot)
        {
            OP.Subscribe($"{Name}.SetAndLoad", (string cmd, object[] param) =>
            {
                if (!SetAndLoad(param,out string reason))
                {
                    EV.PostWarningLog(Module, $"{Name} can not load, {reason}");
                    return false;
                }

                EV.PostInfoLog(Module, $"{Name} start load");
                return true;
            });
            OP.Subscribe($"{Name}.SetAndMap", (string cmd, object[] param) =>
            {
                if (!SetAndMap(param,out string reason))
                {
                    EV.PostWarningLog(Module, $"{Name} can not load, {reason}");
                    return false;
                }

                EV.PostInfoLog(Module, $"{Name} start load");
                return true;
            });

            DATA.Subscribe($"{Name}.CarrierInformation", () => _carrierInformation);


            if (dis == null)
                throw new ArgumentException("DI cannot be null", "diPresent");

            if (indicators == null)
                throw new ArgumentException("DO indicators cannot be null", "diNoWaferProtrude");

            if (string.IsNullOrEmpty(name))
                throw new ArgumentException("name cannot be null or empty", "name");

            _diInfoPadA = new IoSensor[] { dis[0], dis[1] };
            _diInfoPadB = new IoSensor[] { dis[2], dis[3] };
            _diInfoPadC = new IoSensor[] { dis[4], dis[5] };

            _diPresence = dis[6];
            _diWaferProtrude = dis[7];


            _doIndicatorCstPresent = indicators[0];

            _doIndicatorCstPlacement = indicators[1];
            _doIndicatorCstA = indicators[2];
            _doIndicatorCstB = indicators[3];
            _doIndicatorCstC = indicators[4];
            _doIndicatorAlarm = indicators[5];



            IsMapWaferByLoadPort = false;
            PortType = EnumLoadPortType.OpenStage;
            LoadPortType = "OpenStage";
            Initalized = true;
            IsBusy = false;
            Error = false;

            m_InfoPadIndex = ((_diInfoPadA[0].Value && _diInfoPadA[1].Value) ? 1 : 0) +
                ((_diInfoPadB[0].Value && _diInfoPadB[1].Value) ? 2 : 0) +
                ((_diInfoPadC[0].Value && _diInfoPadC[1].Value) ? 4 : 0);

            InfoPadCarrierType = SC.ContainsItem($"CarrierInfo.CarrierName{m_InfoPadIndex}") ?
                        SC.GetStringValue($"CarrierInfo.CarrierName{m_InfoPadIndex}") : "Unknown";
            DockState = FoupDockState.Docked;
            DoorState = FoupDoorState.Open;

            InfoPadCarrierIndex = 3;


        }

        private bool SetAndMap(object[] param,out string reason)
        {
            _isLoaded = true;
            //InfoPadCarrierIndex = Convert.ToInt16(param[0]);
            return MapWafer(out reason);
        }

        private bool SetAndLoad(object[] param,out string reason)
        {
            InfoPadCarrierIndex = Convert.ToInt16(param[0]);
            reason = "";
            return true;
            //return Load(out reason);
        }

        public override bool IsLoaded => _isLoaded;



        protected override bool fStartExecute(object[] param)
        {
            try
            {
                switch (param[0].ToString())
                {
                    case "MapWafer":
                        if (!IsMapWaferByLoadPort)
                        {
                            string reason = "";
                            if (!IsEnableMapWafer(out reason))
                            {
                                EV.PostAlarmLog("LoadPort", $"{LPModuleName} is not ready to map wafer:{reason}");
                                return false;
                            }
                            if (MapRobot != null)
                                return MapRobot.WaferMapping(LPModuleName, out _);
                            return false;
                        }
                        break;
                }
                IsBusy = false;
                return false;
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
                EV.PostAlarmLog(Name, $"Parameter invalid");
                return false;

            }
        }
        public override void Monitor()
        {
            if (_isVirtualMode) return;
            if(SC.ContainsItem($"CarrierInfo.{LPModuleName}CarrierIndex"))
            {
                if ((SC.GetValue<int>($"CarrierInfo.{LPModuleName}CarrierIndex")) != m_InfoPadIndex)
                    SC.SetItemValue($"CarrierInfo.{LPModuleName}CarrierIndex", m_InfoPadIndex);
            }

            //m_InfoPadIndex = ((_diInfoPadA[0].Value && _diInfoPadA[1].Value) ? 1 : 0) +
            //    ((_diInfoPadB[0].Value && _diInfoPadB[1].Value) ? 2 : 0) +
            //    ((_diInfoPadC[0].Value && _diInfoPadC[1].Value) ? 4 : 0);

            m_IndicatorPadIndex = (_diInfoPadA[0].Value ? 32 : 0) + (_diInfoPadA[1].Value ? 16 : 0)
                + (_diInfoPadB[0].Value ? 8 : 0) + (_diInfoPadB[1].Value ? 4 : 0)
                + (_diInfoPadC[0].Value ? 2 : 0) + (_diInfoPadC[1].Value ? 1 : 0);

            switch(m_IndicatorPadIndex)
            {
                case 60:
                    m_InfoPadIndex = 6;
                    break;
                case 48:
                    m_InfoPadIndex = 5;
                    break;
                case 15:
                    m_InfoPadIndex = 3;
                    break;
                default:
                    m_InfoPadIndex = 0;
                    break;

            }


            InfoPadCarrierType = SC.ContainsItem($"CarrierInfo.CarrierName{m_InfoPadIndex}") ?
                        SC.GetStringValue($"CarrierInfo.CarrierName{m_InfoPadIndex}") : "Unknown";
            if (_isPlaced)
            {
                SpecCarrierType = InfoPadCarrierType;
                string carrierinfor = SC.ContainsItem($"CarrierInfo.CarrierInformation{InfoPadCarrierIndex}") ?
                    SC.GetStringValue($"CarrierInfo.CarrierInformation{InfoPadCarrierIndex}") : "";
                string stationName = SC.GetStringValue($"CarrierInfo.{Name}Station{InfoPadCarrierIndex}");
                _carrierInformation = $"Thickness:{carrierinfor},Station Name:{stationName}";
            }
            else
            {
                SpecCarrierType = "";
                _carrierInformation = "";
            }

            _doIndicatorAlarm.SetTrigger(MapError, out _);

            _doIndicatorCstPresent.SetTrigger(_diPresence.Value, out _);


            if (CassetteState == LoadportCassetteState.Absent)
                _trigPresentAbsent.CLK = false;
            if (CassetteState == LoadportCassetteState.Normal)
            {
                if (m_InfoPadIndex == 6)
                {
                    _doIndicatorCstA.SetTrigger(true, out _);
                    _doIndicatorCstB.SetTrigger(false, out _);
                    _doIndicatorCstC.SetTrigger(false, out _);

                    _trigPresentAbsent.CLK = true;
                    _doIndicatorCstPlacement.SetTrigger(true, out _);
                }
                else if (m_InfoPadIndex == 5)
                {
                    _doIndicatorCstA.SetTrigger(false, out _);
                    _doIndicatorCstB.SetTrigger(true, out _);
                    _doIndicatorCstC.SetTrigger(false, out _);

                    _trigPresentAbsent.CLK = true;
                    _doIndicatorCstPlacement.SetTrigger(true, out _);
                }
                else if (m_InfoPadIndex == 3)
                {
                    _doIndicatorCstA.SetTrigger(false, out _);
                    _doIndicatorCstB.SetTrigger(false, out _);
                    _doIndicatorCstC.SetTrigger(true, out _);

                    _trigPresentAbsent.CLK = true;
                    _doIndicatorCstPlacement.SetTrigger(true, out _);
                }
                else
                {
                    _doIndicatorCstA.SetTrigger(false, out _);
                    _doIndicatorCstB.SetTrigger(false, out _);
                    _doIndicatorCstC.SetTrigger(false, out _);
                    _doIndicatorCstPlacement.SetTrigger(false, out _);
                }

               
                            
            }
            else
            {
                _isLoaded = false;
                _doIndicatorCstA.SetTrigger(false, out _);
                _doIndicatorCstB.SetTrigger(false, out _);
                _doIndicatorCstC.SetTrigger(false, out _);
                _doIndicatorCstPlacement.SetTrigger(false, out _);
               
            }

            if (_trigPresentAbsent.R)
            {
                SetPresent(true);
                SetPlaced(true);

            }

            if (_trigPresentAbsent.T)
            {
                SetPresent(false);
                SetPlaced(false);
            }
            var wafers = WaferManager.Instance.GetWafers(LPModuleName);
            int OcrThreshold = SC.GetValue<int>("Process.OCRScoreThreshold");
            foreach(var wafer in wafers)
            {
                if (wafer.IsWaferEmpty)
                    continue;
                if (wafer.SubstE90Status != EnumE90Status.Processed)
                    continue;
                //if (String.IsNullOrEmpty(wafer.LaserMarkerScore))
                //    continue;
                //float waferscore = Convert.ToSingle(wafer.LaserMarkerScore);
                //if(waferscore < (float)OcrThreshold)
                //{
                //    wafer.SubstE90Status = EnumE90Status.Aborted;
                //    wafer.ProcessState = WaferProcessStatus.Failed;
                //}
            }


            base.Monitor();
        }

        public override void OnSlotMapRead(string _slotMap)
        {
            if (!IsMapWaferByLoadPort)
                OnActionDone(new object[] { });

            if (_slotMap.Length != ValidSlotsNumber)
            {
                EV.PostAlarmLog("LoadPort", "Mapping Data Error.");
            }
            int waferindex = 0;

            for (int i = 0; i < _slotMap.Length; i++)
            {
                // No wafer: "0", Wafer: "1", Crossed:"2", Undefined: "?", Overlapping wafers: "W"
                WaferInfoRt wafer = null;
                switch (_slotMap[i])
                {
                    case '0':
                        WaferManager.Instance.DeleteWafer(LPModuleName, i);
                        CarrierManager.Instance.UnregisterCarrierWafer(Name, i);
                        break;
                    case '1':
                        Int32 thickness = Math.Abs(Convert.ToInt32(MapRobot.ReadMappingUpData[waferindex]) - Convert.ToInt32(MapRobot.ReadMappingDownData[waferindex]));
                        Int32 MinThickness = GetMinThickness(_slotMap);
                        if (thickness > (MinThickness * m_doubleWaferFactor))
                        {
                            wafer = WaferManager.Instance.CreateWafer(LPModuleName, i, WaferStatus.Double);
                            WaferManager.Instance.UpdateWaferSize(LPModuleName, i, GetCurrentWaferSize());
                            CarrierManager.Instance.RegisterCarrierWafer(Name, i, wafer);
                            EV.Notify(AlarmLoadPortMapDoubleWafer);
                            waferindex++;
                            break;
                        }


                        wafer = WaferManager.Instance.CreateWafer(LPModuleName, i, WaferStatus.Normal);
                        WaferManager.Instance.UpdateWaferSize(LPModuleName, i, GetCurrentWaferSize());
                        CarrierManager.Instance.RegisterCarrierWafer(Name, i, wafer);

                        waferindex++;
                        break;
                    case '2':
                        wafer = WaferManager.Instance.CreateWafer(LPModuleName, i, WaferStatus.Crossed);
                        WaferManager.Instance.UpdateWaferSize(LPModuleName, i, GetCurrentWaferSize());
                        CarrierManager.Instance.RegisterCarrierWafer(Name, i, wafer);
                        EV.Notify(AlarmLoadPortMapCrossedWafer);

                        waferindex++;
                        break;
                    case 'W':
                        wafer = WaferManager.Instance.CreateWafer(LPModuleName, i, WaferStatus.Double);
                        WaferManager.Instance.UpdateWaferSize(LPModuleName, i, GetCurrentWaferSize());
                        CarrierManager.Instance.RegisterCarrierWafer(Name, i, wafer);
                        EV.Notify(AlarmLoadPortMapDoubleWafer);

                        waferindex++;
                        break;
                    case '?':
                        wafer = WaferManager.Instance.CreateWafer(LPModuleName, i, WaferStatus.Unknown);
                        WaferManager.Instance.UpdateWaferSize(LPModuleName, i, GetCurrentWaferSize());
                        CarrierManager.Instance.RegisterCarrierWafer(Name, i, wafer);
                        //NotifyWaferError(Name, i, WaferStatus.Unknown);

                        waferindex++;
                        break;
                }
            }
            SerializableDictionary<string, object> dvid = new SerializableDictionary<string, object>();

            dvid["SlotMap"] = _slotMap;
            dvid["PortID"] = PortID;
            dvid["PORT_CTGRY"] = SpecPortName;
            dvid["CarrierType"] = SpecCarrierType;
            dvid["CarrierID"] = CarrierId;

            EV.Notify(EventSlotMapAvailable, dvid);
            if (_slotMap.Contains("2"))
            {
                MapError = true;
                EV.Notify(AlarmLoadPortMappingError, new SerializableDictionary<string, object> {
                    {"AlarmText","Mapped Crossed wafer." }
                });
            }
            if (_slotMap.Contains("W"))
            {
                MapError = true;
                EV.Notify(AlarmLoadPortMappingError, new SerializableDictionary<string, object> {
                    {"AlarmText","Mapped Double wafer." }
                });
            }
            if (_slotMap.Contains("?"))
            {
                MapError = true;
                EV.Notify(AlarmLoadPortMappingError, new SerializableDictionary<string, object> {
                    {"AlarmText","Mapped Unknown wafer." }
                });
            }

            if (LPCallBack != null)
                LPCallBack.MappingComplete(_carrierId, _slotMap);

            _isMapped = true;


           
        }

        private Int32 GetMinThickness(string slotmap)
        {
            Int32 thickness =-1;
            int waferindex = 0;
            foreach(char slot in slotmap)
            {
                if(slot != '0')
                {
                    int currentThickness = Math.Abs(Convert.ToInt32(MapRobot.ReadMappingUpData[waferindex]) - Convert.ToInt32(MapRobot.ReadMappingDownData[waferindex]));
                    if (thickness > currentThickness || thickness == -1)
                        thickness = currentThickness;
                    waferindex++;
                }
            }
            return thickness;
        }

        private int m_doubleWaferFactor
        {
            get
            {
                if (SC.ContainsItem($"LoadPort.{LPModuleName}.DoubleWaferFactor"))
                    return SC.GetValue<int>($"LoadPort.{LPModuleName}.DoubleWaferFactor");
                return 2;
            }
        }


        public override void Reset()
        {
            base.Reset();

            //_trigWaferProtrude.RST = true;
        }
        protected override bool fMonitorReset(object[] param)
        {
            IsBusy = false;
            return true;
        }

        public override bool IsEnableMapWafer(out string reason)
        {
            if (CurrentState == LoadPortStateEnum.Error)
            {
                reason = "In Error State";
                return false;
            }
            if (CassetteState != LoadportCassetteState.Normal)
            {
                reason = "no FOUP placed";
                return false;
            }

            if (!_diWaferProtrude.Value && !IsBypassProtrusion && CurrentState != LoadPortStateEnum.TransferBlock)
            {

                //_trigWaferProtrude.CLK = !_diWaferProtrude.Value;

                //if (_trigWaferProtrude.Q)
                //{
                //    EV.Notify(AlarmLoadPortWaferProtrusion);
                //}
                reason = "Found wafer protrude";
                return false;
            }
            if (!_isLoaded)
            {
                reason = "Cassette is not loaded";
                return false;
            }
            //if (!IsReady())
            //{
            //    reason = "Not Ready";
            //    return false;
            //}
            reason = "";
            return true;
        }
        public override bool MapWafer(out string reason)
        {
            _isLoaded = true;

            _trigWaferProtrude.RST = true;
            return base.MapWafer(out reason);
        }

        public override bool Load(out string reason)
        {
            _isLoaded = true;
            reason = "";
            _trigWaferProtrude.RST = true;
            return true;
        }
        
        public override bool IsEnableLoad(out string reason)
        {
            if (CurrentState == LoadPortStateEnum.Error)
            {
                reason = "In Error State";
                return false;
            }
            if (CassetteState != LoadportCassetteState.Normal)
            {
                reason = "no FOUP placed";
                return false;
            }

            if (!_diWaferProtrude.Value && !IsBypassProtrusion && CurrentState != LoadPortStateEnum.TransferBlock)
            {
                //_trigWaferProtrude.CLK = !_diWaferProtrude.Value;

                //if (_trigWaferProtrude.Q)
                //{
                //    EV.Notify(AlarmLoadPortWaferProtrusion);
                //}
                reason = "Found wafer protrude";
                return false;
            }
            reason = "";
            return true;
        }
        public override bool IsEnableTransferWafer(out string reason)
        {
            if (CurrentState == LoadPortStateEnum.Error)
            {
                reason = "In Error State";
                return false;
            }
            if (CassetteState != LoadportCassetteState.Normal)
            {
                reason = "no FOUP placed";
                return false;
            }

            if (!_diWaferProtrude.Value&& !IsBypassProtrusion)
            {
                //_trigWaferProtrude.CLK = !_diWaferProtrude.Value;

                //if (_trigWaferProtrude.Q)
                //{
                //    EV.Notify(AlarmLoadPortWaferProtrusion);
                //}
                reason = "Found wafer protrude";
                return false;
            }
            if (!_isLoaded)
            {
                reason = "Cassette is not loaded";
                return false;
            }

            if (!_isMapped)
            {
                reason = "FOUP not mapped";
                return false;
            }

            foreach(var wafer in WaferManager.Instance.GetWafers(LPModuleName))
            {
                if (wafer.IsWaferEmpty) continue;
                if(wafer.WaferStatus == WaferStatus.Crossed)
                {
                    reason = "Crossed wafer";
                    return false;
                }
                if (wafer.WaferStatus == WaferStatus.Double)
                {
                    reason = "Double wafer";
                    return false;
                }                
            }


            reason = "";
            return true;
        }
        public bool IsBypassProtrusion
        {
            get
            {
                if (SC.ContainsItem($"CarrierInfo.BypassProtrusionDetectCarrier{m_InfoPadIndex}") &&
                    SC.GetValue<bool>($"CarrierInfo.BypassProtrusionDetectCarrier{m_InfoPadIndex}"))
                {
                    return true;
                }
                return false;
            }
            
        }
        public override bool IsForbidAccessSlotAboveWafer()
        {
            return false;
        }

        protected override bool fStartUnload(object[] para)
        {
            if (!_isPlaced) return false;



            //_isMapped = false;


            return true;
        }


        protected override bool fMonitorUnload(object[] param)
        {
            IsBusy = false;
            return true;
        }

        public override bool SetIndicator(Indicator light, IndicatorState state, out string reason)
        {
            reason = "";
            switch (light)
            {
                case Indicator.LOAD:

                    break;
                case Indicator.UNLOAD:

                    break;
                case Indicator.ACCESSAUTO:

                    break;
                case Indicator.ALARM:
                    if (state == IndicatorState.ON) _doIndicatorAlarm.SetTrigger(true, out _);
                    else _doIndicatorAlarm.SetTrigger(false, out _);
                    break;
                default:
                    reason = "Not support";
                    return false;
            }
            return true;
        }

        public bool Disconnect()
        {
            return true;
        }

        protected override bool fStartWrite(object[] param)
        {
            return true;
        }

        protected override bool fStartRead(object[] param)
        {
            return true;
        }


        protected override bool fStartLoad(object[] param)
        {
            string reason;
            //if (!IsEnableLoad(out reason))
            //{
            //    EV.PostAlarmLog("System", $"{LPModuleName} load fail:{reason}");
            //    IsBusy = false;
            //    return false;
            //}
            _isLoaded = true;
            IsBusy = false;
            return true;

        }

        protected override bool fMonitorLoad(object[] param)
        {
            IsBusy = false;
            return true;
        }


        protected override bool fStartInit(object[] param)
        {
            return true;
        }
        protected override bool fMonitorInit(object[] param)
        {
            IsBusy = false;
            return true;
        }
        protected override bool fStartReset(object[] param)
        {

            return true;
        }


    }
}
