using System;
using 常用工具集.Base;
using System.Collections.Generic;
using MES.Utility.Core;
using System.Text;
using System.Linq;
using TwinCAT.Ads.TypeSystem;
using TwinCAT.Ads;

namespace 常用工具集.ViewModel._01PLC通信调试
{
    public class 倍福ADS调试ViewModel : ViewModelBase
    {
        public bool Enabled1 { get; set; } = true;
        public bool Enabled2 { get; set; } = false;
        public string NetId { get; set; } = "192.168.1.10.1.1";
        public int Port { get; set; } = 851;

        public string VarName { get; set; } = "Global_LaserCutting.gHmiPlc.Output.Mes.DeviceStatus.bEMG";
        public int StringLength { get; set; } = 80;
        public string DataType { get; set; } = "";
        public string ReadedValue { get; set; } = "";
        public string Message1 { get; set; } = "";
        public string Message2 { get; set; } = "";

        public string BoolValue { get; set; } = "";

        private AdsServer ads;

        public DelegateCommand ConnectCmd { get; set; }
        public DelegateCommand DisconnectCmd { get; set; }
        public DelegateCommand WriteCmd { get; set; }
        public DelegateCommand ReadCmd { get; set; }
        public 倍福ADS调试ViewModel()
        {
            ConnectCmd = new DelegateCommand(ConnectCmdFunc);
            DisconnectCmd = new DelegateCommand(DisconnectCmdFunc);
            ReadCmd = new DelegateCommand(ReadCmdFunc);
            WriteCmd = new DelegateCommand(WriteCmdFunc);
        }

        private void ConnectCmdFunc(object obj)
        {
            try
            {
                ads = new AdsServer(NetId, Port);
                ads.Connect();
                Enabled1 = false;
                Enabled2 = true;
            }
            catch
            {
                GlobalValues.Error("连接失败");
                return;
            }
        }



        private void DisconnectCmdFunc(object obj)
        {
            ads.Disconnect();
            ads = null;
            Enabled1 = true;
            Enabled2 = false;
        }

        private void ReadCmdFunc(object obj)
        {
            string cmd = obj.ToString();
            ReadedValue = "";
            Message1 = "";
            DataType = "";
            try
            {
                object value = ads.Read(VarName, out string type);
                Message1 = "读取成功";
                DataType = type;
                if (value.GetType().IsArray)
                {
                    Array array = value as Array;
                    List<string> list = new List<string>();
                    for (int i = 0; i < array.Length; i++)
                    {
                        list.Add(array.GetValue(i).ToString());
                    }
                    ReadedValue = list.GetStrArray(" ");
                }
                else
                {
                    ReadedValue = value.ToString();
                }

            }
            catch (Exception ex)
            {
                Message1 = $"读取失败:{ex.Message}";
                return;
            }
        }

        private void WriteCmdFunc(object obj)
        {
            string cmd = obj.ToString();
            try
            {

                if (string.IsNullOrEmpty(BoolValue))
                {
                    Message2 = "请输入数据";
                    return;
                }
                ads.Write(VarName, BoolValue);
                Message2 = $"写入成功";
            }
            catch (Exception ex)
            {
                Message2 = $"写入失败:{ex.Message}";
            }
        }
    }


    public class AdsServer
    {
        Dictionary<string, IAdsSymbol> symbolDict = new Dictionary<string, IAdsSymbol>();

        public delegate void TcAdsStateChangedEventHandler(AdsState state);
        public delegate void TcAdsRouteChangedEventHandler(AmsRouterState state);
        public AmsRouterState AsmRouteState { get; set; } = AmsRouterState.Stop;
        public AdsState AdsState { get; set; } = AdsState.Stop;

        public AdsClient ads;

        public bool SystemIsRunning => ads.IsConnected;

        private TcAdsRouteChangedEventHandler tcAdsSystemStateChangedHandler;
        public event TcAdsRouteChangedEventHandler TcSystemStateChanged
        {
            add
            {
                this.tcAdsSystemStateChangedHandler = (TcAdsRouteChangedEventHandler)Delegate.Combine(this.tcAdsSystemStateChangedHandler, value);
                value(AsmRouteState);
            }
            remove
            {
                this.tcAdsSystemStateChangedHandler = (TcAdsRouteChangedEventHandler)Delegate.Remove(this.tcAdsSystemStateChangedHandler, value);
            }
        }

        private TcAdsStateChangedEventHandler tcAdsClientStateChangedHandler;
        public event TcAdsStateChangedEventHandler TcPlcStateChanged
        {
            add
            {
                this.tcAdsClientStateChangedHandler = (TcAdsStateChangedEventHandler)Delegate.Combine(this.tcAdsClientStateChangedHandler, value);
                value(AdsState);
            }
            remove
            {
                this.tcAdsClientStateChangedHandler = (TcAdsStateChangedEventHandler)Delegate.Remove(this.tcAdsClientStateChangedHandler, value);
            }
        }

        private int _clientPortNr = 801;

        private string _netId = "";

        public AdsServer(string netId, int portNr)
        {
            this._netId = netId;
            this._clientPortNr = portNr;
            ads = new AdsClient();
            ads.RouterStateChanged += Client_AmsRouterNotification;
            ads.AdsStateChanged += Client_AdsStateChanged;
        }

        private void Client_AmsRouterNotification(object sender, AmsRouterNotificationEventArgs e)
        {
            AsmRouteState = e.State;
            tcAdsSystemStateChangedHandler?.Invoke(AsmRouteState);
        }

        public void Connect()
        {
            ads.Connect(this._netId, this._clientPortNr);

        }

        public void Disconnect()
        {
            ads.Disconnect();
            ads.Dispose();
        }

        private void Client_AdsStateChanged(object sender, AdsStateChangedEventArgs e)
        {
            AdsState = e.State.AdsState;
            tcAdsClientStateChangedHandler?.Invoke(AdsState);
        }


        public object Read(string symbolPath, out string oDataType)
        {
            if (!SystemIsRunning)
            {
                throw new Exception("PLC链接失败");
            }
            //bool isArray = symbolPath.Contains("[") && symbolPath.Contains("]");
            //int index1 = symbolPath.IndexOf("["); int index2 = symbolPath.IndexOf("]");
            //uint index = uint.Parse(symbolPath.Substring(index1 + 1, index2 - index1 - 1));
            //symbolPath = symbolPath.Substring(0, index1);

            IAdsSymbol symbol = null;
            if (symbolDict.ContainsKey(symbolPath))
            {
                symbol = symbolDict[symbolPath];
            }
            else
            {
                symbol = ads.ReadSymbol(symbolPath);
                if (symbol == null)
                    throw new Exception("找不到该变量");
                symbolDict.Add(symbolPath, symbol);
            }
            oDataType = symbol.TypeName;
            if (symbol.TypeName.Substring(0, 3) != "ARR")
            {
                AdsDataTypeId datatype = symbol.DataTypeId;
                //BOOL
                if (datatype == AdsDataTypeId.ADST_BIT)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(bool));
                //byte
                if (datatype == AdsDataTypeId.ADST_UINT8)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(byte));
                //sbyte
                if (datatype == AdsDataTypeId.ADST_INT8)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(sbyte));
                //ushort
                if (datatype == AdsDataTypeId.ADST_UINT16)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(ushort));
                //short
                if (datatype == AdsDataTypeId.ADST_INT16)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(short));
                if (datatype == AdsDataTypeId.ADST_UINT32)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(uint));
                if (datatype == AdsDataTypeId.ADST_INT32)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(int));
                if (datatype == AdsDataTypeId.ADST_REAL32)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(float));
                if (datatype == AdsDataTypeId.ADST_REAL64)
                    return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(double));
                if (datatype == AdsDataTypeId.ADST_STRING)
                    return ads.ReadAnyString((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, symbol.Size, Encoding.Default);
                if (datatype == AdsDataTypeId.ADST_WSTRING)
                {
                    Memory<byte> stream = new Memory<byte>(new byte[symbol.Size]);
                    int readLength = ads.Read((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, stream); 
                    byte[] bytes = stream.ToArray().ToList().Take(readLength).ToArray();
                    int charCount = readLength / 2;
                    char[] charArray = new char[charCount];
                    for (int i = 0; i < charCount; i++)
                    {
                        char value = (char)(bytes[i * 2 + 1] << 8 | bytes[i * 2]);
                        charArray[i] = value;
                        if (value == 0)
                            break;
                    }
                    return new String(charArray).Trim('\0');
                }
                if (datatype == AdsDataTypeId.ADST_BIGTYPE)
                {
                    if (symbol.TypeName.StartsWith("TIME") || symbol.TypeName.StartsWith("DATE") || symbol.TypeName.StartsWith("TOD"))
                    {
                        return ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, typeof(DateTime));
                    }
                }
            }
            else
            {
                AdsDataTypeId datatype = symbol.DataTypeId;
                if (datatype == AdsDataTypeId.ADST_BIT)
                {
                    //if (isArray)
                    //{
                    //    return (bool)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + index, typeof(bool));
                    //}
                    bool[] values = new bool[symbol.Size];
                    for (uint i = 0; i < symbol.Size; i++)
                    {
                        values[i] = (bool)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + i, typeof(bool));
                    }
                    return values;
                }

                //byte
                if (datatype == AdsDataTypeId.ADST_UINT8)
                {
                    //if (isArray)
                    //{
                    //    return (byte)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + index, typeof(byte)); ;
                    //}
                    byte[] values = new byte[symbol.Size];
                    for (uint i = 0; i < symbol.Size; i++)
                    {
                        values[i] = (byte)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + i, typeof(byte));
                    }
                    return values;
                }
                //sbyte
                if (datatype == AdsDataTypeId.ADST_INT8)
                {
                    //if (isArray)
                    //{
                    //    return (sbyte)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + index, typeof(sbyte));
                    //}
                    sbyte[] values = new sbyte[symbol.Size];
                    for (uint i = 0; i < symbol.Size; i++)
                    {
                        values[i] = (sbyte)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + i, typeof(sbyte));
                    }
                    return values;
                }
                //ushort
                if (datatype == AdsDataTypeId.ADST_UINT16)
                {
                    //if (isArray)
                    //{
                    //    return (ushort)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 2U), typeof(ushort));
                    //}
                    int count = symbol.Size / 2;
                    ushort[] values = new ushort[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (ushort)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 2U), typeof(ushort));
                    }
                    return values;
                }
                //short
                if (datatype == AdsDataTypeId.ADST_INT16)
                {
                    //if (isArray)
                    //{
                    //    return (short)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 2U), typeof(short));
                    //}
                    int count = symbol.Size / 2;
                    short[] values = new short[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (short)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 2U), typeof(short));
                    }
                    return values;
                }
                if (datatype == AdsDataTypeId.ADST_UINT32)
                {
                    //if (isArray)
                    //{
                    //    return (uint)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 4U), typeof(uint));
                    //}
                    int count = symbol.Size / 4;
                    uint[] values = new uint[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (uint)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 4U), typeof(uint));
                    }
                    return values;
                }
                if (datatype == AdsDataTypeId.ADST_INT32)
                {
                    //if (isArray)
                    //{
                    //    return (int)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 4U), typeof(int));
                    //}
                    int count = symbol.Size / 4;
                    int[] values = new int[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (int)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 4U), typeof(int));
                    }
                    return values;
                }
                if (datatype == AdsDataTypeId.ADST_REAL32)
                {
                    //if (isArray)
                    //{
                    //    return (float)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 4U), typeof(float));
                    //}
                    int count = symbol.Size / 4;
                    float[] values = new float[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (float)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 4U), typeof(float));
                    }
                    return values;
                }
                if (datatype == AdsDataTypeId.ADST_REAL64)
                {
                    //if (isArray)
                    //{
                    //    return (double)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 8U), typeof(double));
                    //}
                    int count = symbol.Size / 8;
                    double[] values = new double[count];
                    for (uint i = 0; i < count; i++)
                    {
                        values[i] = (double)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 8U), typeof(double));
                    }
                    return values;
                }
                //TC3 字符串数组为 ADST_STRING
                if (datatype == AdsDataTypeId.ADST_STRING)
                {
                    int every = 81;
                    if (symbol.TypeName.Contains("OF STRING"))
                    {
                        int index = symbol.TypeName.IndexOf("OF STRING(");
                        if (index != 1)
                        {
                            int index2 = symbol.TypeName.LastIndexOf(")");
                            every = Convert.ToInt32(symbol.TypeName.Substring(index + 10, index2 - index - 10)) + 1;
                        }
                        int count = symbol.Size / every;
                        string[] values = new string[count];
                        for (uint i = 0; i < count; i++)
                        {
                            values[i] = ads.ReadAnyString((uint)symbol.IndexGroup, (uint)(symbol.IndexOffset + (i * every)), every, Encoding.Default);
                        }
                        return values;
                    }
                }
                if (datatype == AdsDataTypeId.ADST_WSTRING)
                {
                    int every = 512;
                    if (symbol.TypeName.Contains("OF WSTRING"))
                    {
                        int index = symbol.TypeName.IndexOf("OF WSTRING(");
                        if (index != 1)
                        {
                            int index2 = symbol.TypeName.LastIndexOf(")");
                            every = (Convert.ToInt32(symbol.TypeName.Substring(index + 11, index2 - index - 11)) + 1) * 2;
                        }
                        int count = symbol.Size / every;
                        string[] values = new string[count];
                        for (uint i = 0; i < count; i++)
                        {
                            Memory<byte> stream = new Memory<byte>(new byte[every]);
                            int readLength = ads.Read((uint)symbol.IndexGroup, (uint)(symbol.IndexOffset + (i * every)), stream);
                            byte[] bytes = stream.ToArray().ToList().Take(readLength).ToArray();
                            int charCount = readLength / 2;
                            char[] charArray = new char[charCount];
                            for (int j = 0; j < charCount; j++)
                            {
                                char value = (char)(bytes[j * 2 + 1] << 8 | bytes[j * 2]);
                                charArray[j] = value;
                                if (value == 0)
                                    break;
                            }
                            values[i] = new String(charArray).Trim('\0');
                        }
                        return values;
                    }
                }
                if (datatype == AdsDataTypeId.ADST_BIGTYPE)
                {
                    //TC2 字符串为ADST_BIGTYPE
                    if (symbol.TypeName.Contains("OF STRING"))
                    {
                        //if (isArray)
                        //{
                        //    return ads.ReadAnyString((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 81U), 81, Encoding.Default);
                        //}
                        int count = symbol.Size / 81;
                        string[] values = new string[count];
                        for (uint i = 0; i < count; i++)
                        {
                            values[i] = ads.ReadAnyString((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 81U), 81, Encoding.Default);
                        }
                        return values;
                    }

                    if (symbol.TypeName.Contains("OF TIME") || symbol.TypeName.Contains("OF DATE"))
                    {
                        //if (isArray)
                        //{
                        //    return (DateTime)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 4), typeof(DateTime));
                        //}
                        int count = symbol.Size / 4;
                        DateTime[] values = new DateTime[count];
                        for (uint i = 0; i < count; i++)
                        {
                            values[i] = (DateTime)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 4), typeof(DateTime));
                        }
                        return values;
                    }
                    if (symbol.TypeName.Contains("OF TOD"))
                    {
                        //if (isArray)
                        //{
                        //    return (DateTime)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (index * 4), typeof(DateTime));
                        //}
                        int count = symbol.Size / 4;
                        DateTime[] values = new DateTime[count];
                        for (uint i = 0; i < count; i++)
                        {
                            values[i] = (DateTime)ads.ReadAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset + (i * 4), typeof(DateTime));
                        }
                        return values;
                    }
                }

            }
            throw new Exception("不支持的数据类型");
        }

        public void Write(string symbolPath, string value)
        {
            if (!SystemIsRunning)
            {
                throw new Exception("PLC链接失败");
            }
            IAdsSymbol symbol = null;
            if (symbolDict.ContainsKey(symbolPath))
            {
                symbol = symbolDict[symbolPath];
            }
            else
            {
                symbol = ads.ReadSymbol(symbolPath);
                if (symbol == null)
                    throw new Exception("找不到该变量");
                symbolDict.Add(symbolPath, symbol);
            }
            if (symbol.TypeName.Substring(0, 3) != "ARR")
            {
                AdsDataTypeId datatype = symbol.DataTypeId;
                //BOOL
                if (datatype == AdsDataTypeId.ADST_BIT)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, bool.Parse(value));
                    return;
                }
                //byte
                if (datatype == AdsDataTypeId.ADST_UINT8)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, byte.Parse(value));
                    return;
                }
                //sbyte
                if (datatype == AdsDataTypeId.ADST_INT8)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, sbyte.Parse(value));
                    return;
                }
                //ushort
                if (datatype == AdsDataTypeId.ADST_UINT16)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, ushort.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_INT16)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, short.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_UINT32)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, uint.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_INT32)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, int.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_REAL32)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, float.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_REAL64)
                {
                    ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, double.Parse(value));
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_STRING)
                {
                    ads.WriteAnyString(symbolPath, value, symbol.Size,Encoding.Default);
                    //ads.WriteAnyString((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, value, symbol.Size, Encoding.Default);
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_WSTRING)
                {
                    char[] charArray = value.ToCharArray();
                    byte[] bytes = new byte[charArray.Length * 2];
                    for (int i = 0; i < charArray.Length; i++)
                    {
                        bytes[i * 2] = (byte)(charArray[i] & 0xFF);
                        bytes[i * 2 + 1] = (byte)(charArray[i] >> 8 & 0xFF);
                    }
                    if (bytes.Length > symbol.Size)
                    {
                        ads.Write((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, new ReadOnlyMemory<byte>(bytes));
                    }
                    else
                    {
                        byte[] newBytes = new byte[symbol.Size];
                        for (int i = 0; i < bytes.Length; i++)
                        {
                            newBytes[i] = bytes[i];
                        }
                        ads.Write((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, new ReadOnlyMemory<byte>(newBytes)); 
                    }
                    return;
                }
                if (datatype == AdsDataTypeId.ADST_BIGTYPE)
                {
                    if (symbol.TypeName.StartsWith("TIME") || symbol.TypeName.StartsWith("DATE") || symbol.TypeName.StartsWith("TOD"))
                    {
                        bool flag = DateTime.TryParse(value, out DateTime result);
                        if (!flag)
                            throw new Exception("格式转换失败");
                        ads.WriteAny((uint)symbol.IndexGroup, (uint)symbol.IndexOffset, result);
                        return;
                    }
                }
                throw new Exception("不支持的数据类型");
            }
            else
            {
                throw new Exception("不支持数组批量写入");
            }
        }
    }

}
