using Dsu.PLC;
using Dsu.PLC.Common;
using Dsu.PLC.Melsec;
using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using System.Reflection;
using System.Threading.Tasks;
using SystemX.Net.Platform.Common.Event;
using SystemX.Net.Platform.Common.Util;

namespace SystemX.PLC.Interface.McProtocol
{
    public class PLCDeviceMXC
    {
        public FunctionEventHandler FuncEvtHndl { get; set; }
        public string CPU_TYPE_STR { set; get; } //Example: "Q03UDECPU";        
        public string PLC_IP_ADDR { set; get; }
        public int PLC_PORT_NUMBER { set; get; }
        public string CONNECTION_TYPE_STR { set; get; }
        public int TIMEOUT { set; get; }
        public bool READPORT { set; get; }
        public string DeviceID { set; get; }

        private MxConnection _conn;  // Read port UDP   Write port TCP

        public bool DevClose()
        {
            return _conn.Disconnect();
        }

        public bool DevOpen()
        {
            try
            {
                TransportProtocol ConType = CONNECTION_TYPE_STR.ToUpper() == "TCP" ? TransportProtocol.Tcp : TransportProtocol.Udp;
                _conn = new MxConnection(new MxConnectionParameters(PLC_IP_ADDR, Convert.ToUInt16(PLC_PORT_NUMBER), ConType));
                return _conn.Connect();
            }
            catch (Exception ex)
            {
                LogMessage.MessageOutput.ConsoleWrite(ex.Message, ConsoleColor.Red, LogMessage.LogMessageLevel.FATAL);

                return false;
            }
        }

        public bool AddDevices(string deviceNames)
        {
            _conn.CreateTag(deviceNames);

            return true;
        }

        public async Task SingleScanStartAsync()
        {
            if (_conn.Tags.Count == 0)
                return;

            int readCount = 0;

            _conn.PerRequestDelay = TIMEOUT;

            _conn.Subject.OfType<TagValueChangedEvent>().Subscribe(evt =>
            {
                readCount++;
                EventTag((MxTag)evt.Tag);
            });

            _conn.StartDataExchangeLoopAsync();  // Do not use await 

            // _conn.Disconnect();

            Stopwatch xSW = new Stopwatch();
            xSW.Start();
            bool bSuccess = true;

            while (true)
            {
                if (_conn.Tags.Count <= readCount)
                    break;
                else
                    System.Threading.Thread.Sleep(50);

                if (xSW.ElapsedMilliseconds > 5000)
                {
                    bSuccess = false;
                    break;
                }
            }

            if (!bSuccess)
            {
                LogMessage.MessageOutput.ConsoleWrite("Trouble in Single Scan Start Async", ConsoleColor.White, LogMessage.LogMessageLevel.FATAL);

                //await SingleScanStartAsync();
            }
        }

        private void EventTag(MxTag tag)
        {
            if (tag.IsBitDevice)
                FuncEvtHndl.DoReceive(this, tag.Name + ";" + (Convert.ToBoolean(tag.Value) ? 1 : 0).ToString());
            else
                FuncEvtHndl.DoReceive(this, tag.Name + ";" + tag.Value.ToString());
        }

        public bool WriteBitDevice(string strDeviceWrite, int[] value)
        {
            //Console.WriteLine(string.Format("{0} : {1}", strDeviceWrite, value[0]));
            return _conn.McProtocol.SetBitDevice(strDeviceWrite, 1, value) == 0;
        }

        public bool WriteDevice(string strDeviceWrite, int value)
        {
            //Console.WriteLine(string.Format("{0} : {1}", strDeviceWrite, value));
            return _conn.McProtocol.SetDevice(strDeviceWrite, Convert.ToInt32(value)) == 0;
        }

        public bool WriteBlockDevice(string strDeviceWrite, int nLength, int[] nValues)
        {
            //Console.WriteLine(string.Format("{0} : {1}", strDeviceWrite, value));
            return _conn.McProtocol.WriteDeviceBlock(strDeviceWrite, nLength, nValues) == 0;
        }

        public int ReadDevice(string strDeviceRead)
        {
            int value;
            _conn.McProtocol.GetDevice(strDeviceRead, out value);
            return value;
        }

        public int[] ReadBlockDevice(string strDeviceRead, int nLength)
        {
            try
            {
                int[] anvalue = new int[nLength];

                int nResult = _conn.McProtocol.ReadDeviceBlock(strDeviceRead, nLength, anvalue);

                return anvalue;
            }
            catch (Exception ex)
            {
                LogMessage.MessageOutput.ConsoleWrite($"Trouble in McComponent during ReadBlockDevice:{strDeviceRead} - {nLength}", ConsoleColor.White, LogMessage.LogMessageLevel.FATAL);
                LogMessage.MessageOutput.ConsoleWrite($"\t- Error Message {ex.Message}", ConsoleColor.White, LogMessage.LogMessageLevel.FATAL);

                return new int[1];
            }
        }
        public string GetModuleName()
        {
            return Assembly.GetExecutingAssembly().ManifestModule.Name.Replace(".dll", string.Empty);
        }
    }
}
