using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.Core.Address;
using HslCommunication.Core.IMessage;
using HslCommunication.Core;
using HslCommunication;
using HslCommunication.Profinet.Siemens;
using HslCommunication.Reflection;
using PVDEMCS.Common.Tools;
using PVDEMCS.Devices;
using PVDEMCS.Common;

namespace PVDEMCS.Devices.Impl
{
    /// <summary>
    /// PLC通信服务 SiemensS7 smart200 实现
    /// </summary>
    internal class S7CommunicationService : IPLCCommunicationService
    {
        private SiemensS7Net siemensS7Net = null;

        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected { get; private set; }

        public S7CommunicationService(string deviceProtocol)
        {
            switch (deviceProtocol)
            {
                case DeviceProtocol.S7_400:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S400);
                    break;
                case DeviceProtocol.S7_1200:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S1200);
                    break;
                case DeviceProtocol.S7_1500:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500);
                    break;
                case DeviceProtocol.S7_200:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S200);
                    break;
                case DeviceProtocol.S7_300:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S300);
                    break;
                case DeviceProtocol.S7_200Smart:
                    siemensS7Net = new SiemensS7Net(SiemensPLCS.S200Smart);
                    break;
                default:
                    throw new ArgumentException($"DeviceProtocol 参数不正确，没有{deviceProtocol}协议");
            }
            siemensS7Net.ConnectTimeOut = 3000;
        }

        #region 同步
        /// <summary>
        /// 连接PLC（长连接）
        /// </summary>
        /// <param name="address"></param>
        /// <param name="port"></param>
        public Result Connection(string address, int port = 0)
        {
            siemensS7Net.IpAddress = address;
            if (port > 0)
            {
                siemensS7Net.Port = port;
            }
            var operateResult = siemensS7Net.ConnectServer();
            var result = ConvertResult(operateResult);
            if (result.IsSuccess)
            {
                IsConnected = true;
            }
            return result;
        }

        /// <summary>
        /// 关闭连接PLC（长连接）
        /// </summary>
        public Result ColseConnection()
        {
            var operateResult = siemensS7Net.ConnectClose();
            var result = ConvertResult(operateResult);
            if (result.IsSuccess)
            {
                IsConnected = false;
            }
            return result;
        }
        /// <summary>
        /// 读取指定地址的byte[]值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<byte[]> ReadBytes(string address, ushort length)
        {
            var operateResult = siemensS7Net.Read(address, length);
            var result = ConvertResult(operateResult);
            return result;
        }
        /// <summary>
        /// 读取指定地址的bool值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<bool> ReadBool(string address)
        {
            var operateResult = siemensS7Net.ReadBool(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的byte值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<byte> ReadByte(string address)
        {
            var operateResult = siemensS7Net.ReadByte(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Int16值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<short> ReadInt16(string address)
        {
            var operateResult = siemensS7Net.ReadInt16(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Int32值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<int> ReadInt32(string address)
        {
            var operateResult = siemensS7Net.ReadInt32(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的long值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public Result<long> ReadLong(string address)
        {
            var operateResult = siemensS7Net.ReadInt64(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Float值
        /// </summary>
        /// <returns></returns>
        public Result<float> ReadFloat(string address)
        {
            var operateResult = siemensS7Net.ReadFloat(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的double值
        /// </summary>
        /// <returns></returns>
        public Result<double> ReadDouble(string address)
        {
            var operateResult = siemensS7Net.ReadDouble(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入bool值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, bool value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入byte值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, byte value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;

        }

        /// <summary>
        /// 写入Int16值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, short value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入Int32值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, int value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入float值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, float value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入double值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, double value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入long值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public Result Write(string address, long value)
        {
            var operateResult = siemensS7Net.Write(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        #endregion

        #region 异步
        /// <summary>
        /// 连接PLC（长连接）
        /// </summary>
        /// <param name="address"></param>
        /// <param name="port"></param>
        public async Task<Result> ConnectionAsync(string address, int port = 0)
        {
            siemensS7Net.IpAddress = address;
            if (port > 0)
            {
                siemensS7Net.Port = port;
            }
            var operateResult = await siemensS7Net.ConnectServerAsync();
            var result = ConvertResult(operateResult);
            if (operateResult.IsSuccess)
            {
                IsConnected = true;
            }
            return result;
        }

        /// <summary>
        /// 关闭连接PLC（长连接）
        /// </summary>
        public async Task<Result> ColseConnectionAsyn()
        {
            var operateResult = await siemensS7Net.ConnectCloseAsync();
            var result = ConvertResult(operateResult);
            if (operateResult.IsSuccess)
            {
                IsConnected = false;
            }
            return result;
        }
        /// <summary>
        /// 读取指定地址的byte[]值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<byte[]>> ReadBytesAsync(string address, ushort length)
        {
            var operateResult = await siemensS7Net.ReadAsync(address, length);
            var result = ConvertResult(operateResult);
            return result;
        }
        /// <summary>
        /// 读取指定地址的bool值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<bool>> ReadBoolAsync(string address)
        {
            var operateResult = await siemensS7Net.ReadBoolAsync(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的byte值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<byte>> ReadByteAsync(string address)
        {
            var operateResult = await siemensS7Net.ReadByteAsync(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Int16值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<short>> ReadInt16Async(string address)
        {
            var operateResult = await siemensS7Net.ReadInt16Async(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Int32值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<int>> ReadInt32Async(string address)
        {
            var operateResult = await siemensS7Net.ReadInt32Async(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的long值
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public async Task<Result<long>> ReadLongAsync(string address)
        {
            var operateResult = await siemensS7Net.ReadInt64Async(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的Float值
        /// </summary>
        /// <returns></returns>
        public async Task<Result<float>> ReadFloatAsync(string address)
        {
            var operateResult = await siemensS7Net.ReadFloatAsync(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 读取指定地址的double值
        /// </summary>
        /// <returns></returns>
        public async Task<Result<double>> ReadDoubleAsync(string address)
        {
            var operateResult = await siemensS7Net.ReadDoubleAsync(address);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入bool值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, bool value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入byte值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, byte value)
        {

            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入Int16值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, short value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入Int32值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, int value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入float值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, float value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入double值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, double value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        /// <summary>
        /// 写入long值
        /// </summary>
        /// <param name="address">写入地址</param>
        /// <param name="value"></param>
        public async Task<Result> WriteAsync(string address, long value)
        {
            var operateResult = await siemensS7Net.WriteAsync(address, value);
            var result = ConvertResult(operateResult);
            return result;
        }

        #endregion


        /// <summary>
        /// 转换 result （将框架结果结构转换成当前程序结果结构）
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>

        private Result ConvertResult(OperateResult result)
        {
            var retResult = ModelTools.PubClone<OperateResult, Result>.Trans(result);
            return retResult;
        }

        /// <summary>
        /// 转换 result （将框架结果结构转换成当前程序结果结构）
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>

        private Result<T> ConvertResult<T>(OperateResult<T> result)
        {
            VerifyTimeOut(result);

            var retResult = ModelTools.PubClone<OperateResult<T>, Result<T>>.Trans(result);
            retResult.Content = result.Content;
            return retResult;
        }

        /// <summary>
        /// 判断连接是否中断
        /// </summary>
        /// <param name="result"></param>
        private void VerifyTimeOut(OperateResult result) 
        {
            var msg = result.Message;
            
            if (result!=null && !result.IsSuccess)
            {
                var isTimeOut = msg.Contains("连接") && msg.Contains("失败") && msg.Contains("超时时间为");
                if (isTimeOut) 
                {
                    IsConnected = false;
                }
            }
        }
    }
}
