using Masuit.Tools;
using PVDEMCS.Common;
using PVDEMCS.Common.Log;
using PVDEMCS.Devices;
using PVDEMCS.Services.Models;
using System.Security.Cryptography;

namespace PVDEMCS.Devices.Impl
{
    /// <summary>
    /// 控制器点位监控
    /// </summary>
    public class DeviceMonitor
    {
        private DeviceInfo deviceInfo;
        private IPLCCommunicationService plcCommunicationService;
        private List<DevicePoint> devicePoints;
        private object lockObj = new object();
        private bool isRun = false;
        public DeviceMonitor(DeviceInfo deviceInfo)
        {
            this.deviceInfo = deviceInfo;
            devicePoints = new List<DevicePoint>();
            PointMonitor();
        }
        /// <summary>
        /// PLC 点位改变触发
        /// </summary>
        public event EventHandler<DevicePoint> PointChange;

        /// <summary>
        /// 获取错误相信
        /// </summary>
        public event EventHandler<string> ErrorMessage;

        /// <summary>
        /// 控制器编号
        /// </summary>
        public string DeviceCode
        {
            get
            {
                return deviceInfo.DeviceCode;
            }
        }

        /// <summary>
        /// 间隔时间
        /// </summary>
        public int Interval { get; set; } = 2000;
        public IPLCCommunicationService PLCCommunicationService
        {
            get
            {
                return plcCommunicationService;
            }
        }

        /// <summary>
        /// 连接状态
        /// </summary>
        public bool IsConnected
        {
            get
            {
                return plcCommunicationService.IsConnected;
            }
        }

        /// <summary>
        /// 监控是否运行
        /// </summary>
        public bool IsRun
        {
            get { return isRun; }
        }

        /// <summary>
        /// 当前点位集合
        /// </summary>
        public ICollection<DevicePoint> Points
        {
            get
            {
                return devicePoints;
            }
        }

        /// <summary>
        /// PLC地址与端口
        /// </summary>
        public void Init(string address, int port = 0)
        {
            deviceInfo.Host = address;
            deviceInfo.Port = port;
        }

        /// <summary>
        /// 获取PLC通信接口
        /// </summary>
        /// <param name="plcCommunicationService"></param>
        public void Init(IPLCCommunicationService plcCommunicationService)
        {
            this.plcCommunicationService = plcCommunicationService;

        }

        /// <summary>
        ///  加载点位集合
        /// </summary>
        /// <param name="points"></param>
        public void Load(params DevicePoint[] points)
        {
            if (points.IsNullOrEmpty())
            {
                throw new ArgumentException($"{nameof(points)}: 不能为空！");
            }
            if (devicePoints.Where(f => points.Select(ff => ff.PointCode).Contains(f.PointCode)).Count() > 0)
            {
                throw new ArgumentException($"{nameof(points)}: PointCode 不能重复！");
            }
            devicePoints.AddRange(points);
        }

        /// <summary>
        /// 清理所有点位
        /// </summary>
        public void Clear()
        {
            devicePoints.Clear();
        }

        /// <summary>
        /// 开始监控
        /// </summary>
        public async Task<bool> StartAsync()
        {
            if (plcCommunicationService == null)
            {
                plcCommunicationService = new S7CommunicationService(deviceInfo.Protocol);
            }
            if (!plcCommunicationService.IsConnected)
            {
                var result = await plcCommunicationService.ConnectionAsync(deviceInfo.Host, deviceInfo.Port);
                if (!result.IsSuccess)
                {
                    ErrorMessage?.Invoke(this, "开始PLC连接失败：" + result.Message);
                    return await Task.FromResult(false);
                }

                isRun = true;
                $"PLC控制器：{deviceInfo.DeviceCode}连接成功".WriteInfoLog();
            }
            return await Task.FromResult(true);
        }

        /// <summary>
        /// 停止监控
        /// </summary>
        public async Task<bool> StopAsync()
        {
            if (plcCommunicationService.IsConnected)
            {
                var result = await plcCommunicationService.ColseConnectionAsyn();
                if (!result.IsSuccess)
                {
                    ErrorMessage?.Invoke(this, "关闭PLC连接失败：" + result.Message);
                    return await Task.FromResult(false);
                }
                isRun = false;
                return await Task.FromResult(true);

            }
            return await Task.FromResult(true);
        }

        private void PointMonitor()
        {
            Task.Run(() =>
            {
                while (true)
                {
                    lock (lockObj)
                    {
                        if (Points.Count > 0 && isRun && plcCommunicationService.IsConnected)
                        {
                            devicePoints.ForEach(async point => 
                            {
                                await CallDevicePoint(point);
                            });
                        }
                        Thread.Sleep(Interval);
                    }
                }
            });
        }

        /// <summary>
        /// 获取PLC点位信息
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        private async Task CallDevicePoint(DevicePoint point)
        {
            var type = Type.GetType(point.DataType);
            var plcService = plcCommunicationService;
            //Boolean
            if (type.Equals(typeof(bool)))
            {
                var result = await plcService.ReadBoolAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //Byte
            if (type.Equals(typeof(byte)))
            {
                var result = await plcService.ReadByteAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //Byte
            if (type.Equals(typeof(byte)))
            {
                var result = await plcService.ReadByteAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //Int16
            if (type.Equals(typeof(short)))
            {
                var result = await plcService.ReadInt16Async(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //Int32
            if (type.Equals(typeof(int)))
            {
                var result = await plcService.ReadInt32Async(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //Int64
            if (type.Equals(typeof(long)))
            {
                var result = await plcService.ReadLongAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //float
            if (type.Equals(typeof(float)))
            {
                var result = await plcService.ReadFloatAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
            //double
            if (type.Equals(typeof(double)))
            {
                var result = await plcService.ReadDoubleAsync(point.Address);
                GetReturnValue(point, result);
                return;
            }
        }

        private void GetReturnValue<T>(DevicePoint point, Result<T> result)
        {
            if (result.IsSuccess)
            {
                if (point.ObjectValue == null || !point.ObjectValue.Equals(result.Content))
                {
                    point.ObjectValue = result.Content;
                    PointChange?.Invoke(this, point);
                    $"点位发生变更：{point.ToJsonString()}".WriteInfoLog();
                }
            }
            else
            {
                ErrorMessage?.Invoke(this, result.Message);
            }
        }
    }
}
