using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using log4net;

namespace ChuPiao.Common.Communication
{
    /// <summary>
    /// Socket通讯客户端
    /// </summary>
    public interface ISocketClient
    {
        /// <summary>
        /// 提交请求
        /// </summary>
        /// <param name="requestMsg"></param>
        /// <returns></returns>
        string CommitRequest(string requestMsg);

        /// <summary>
        /// 关闭客户端
        /// </summary>
        void Close();
    }
    /// <summary>
    /// Socket通讯客户端
    /// </summary>
    public class SocketClientOfLotmachine : ISocketClient
    {
        private IPEndPoint _remoteIpEndPoint;
        private Socket _socket;
        private int _receiveTimeout;
        private int _sendTimeout;
        //private int _pingIdleTime;
        //private string _pingMsg;
        //private Thread _pingThread;
        private ILog _logger;
        //private bool _pingIsRun;
        private int _retryNum = 1;
        private NetworkStream _networkStream;
        private Encoding _charset;

        //最后交易时间
        private volatile int _lastTranslation;

        public SocketClientOfLotmachine(string host, int port, int receiveTimeout, int sendTimeout, ILog logger)
        {
            _remoteIpEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
            _receiveTimeout = receiveTimeout;
            _sendTimeout = sendTimeout;
            _logger = logger;
            _charset = Encoding.UTF8;
        }
        /// <summary>
        /// 发送消息包
        /// </summary>
        /// <param name="message"></param>
        private void Send(string message)
        {
            byte[] contentData = _charset.GetBytes(message);
            //因为BitConverter.GetBytes是按小端字节序转换成byte数组，而网络字节序是大端字节序，所以进行转换
            byte[] headerData = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(contentData.Length));
            _networkStream.Write(headerData, 0, headerData.Length);
            _networkStream.Write(contentData, 0, contentData.Length);
            _networkStream.Flush();
        }
        /// <summary>
        /// 读取回复消息包
        /// </summary>
        /// <returns></returns>
        private string Read()
        {
            string content = string.Empty;
            byte[] buffer = new byte[4];
            _networkStream.Read(buffer, 0, buffer.Length);
            //因为BitConverter.GetBytes是按小端字节序转换成byte数组，而网络字节序是大端字节序，所以进行转换
            int contentLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0));
            if (contentLength > 0)
            {
                byte[] contentData = new byte[contentLength];

                int offset = 0;
                int num = 0;
                do
                {
                    num = _networkStream.Read(contentData, offset, contentLength - offset);
                    offset += num;
                } while (num > 0 && offset < contentLength);
                if (offset != contentLength)
                {
                    throw new Exception("交易包长度与实际交易内容长度不符");
                }
                _lastTranslation = Environment.TickCount;
                content = _charset.GetString(contentData);
            }
            return content;
        }

        /// <summary>
        /// 连接到远程服务器
        /// </summary>
        private void Connect()
        {
            for (int i = 0; i < _retryNum; i++)
            {
                try
                {
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _socket.SendTimeout = _sendTimeout;
                    _socket.ReceiveTimeout = _receiveTimeout;
                    _socket.NoDelay = true;

                    //设置 TCP 心跳，空闲 15 秒，每 5 秒检查一次
                    //uint dummy = 0;
                    //byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
                    //BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);//是否启用Keep-Alive
                    //BitConverter.GetBytes((uint)1000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
                    //BitConverter.GetBytes((uint)1000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔
                    //_socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);

                    _socket.Connect(_remoteIpEndPoint);
                    _logger.Info(string.Concat("连接到", _remoteIpEndPoint.ToString(), "成功！"));
                    _lastTranslation = Environment.TickCount;//更新最后交易时间
                    if (_socket.Connected)
                    {
                        break;
                    }
                }
                catch (Exception exception)
                {
                    _logger.Info(string.Concat("连接到", _remoteIpEndPoint.ToString(), "异常。错误信息：", exception));
                    Console.WriteLine(string.Concat("异常：", exception));

                    if (i == _retryNum - 1)
                    {
                        throw new IOException(string.Concat("连接到", _remoteIpEndPoint.ToString(), " ", _retryNum, "次失败"));
                    }
                    _logger.Info(string.Concat("尝试第", i + 1, "次连接到", _remoteIpEndPoint.ToString()));
                }
            }
            if (Connected)
            {
                _networkStream = new NetworkStream(_socket, true);
            }
        }
        /// <summary>
        /// 重新连接
        /// </summary>
        private void ReConnect()
        {
            _logger.Info("连接断掉，正在进行重连……");
            Console.WriteLine("连接断掉，正在进行重连……");
            try
            {
                _socket.Close();
            }
            catch (IOException e)
            {
                _logger.Error(e);
            }
            try
            {
                Connect();
            }
            catch (IOException e)
            {
                _logger.Error(e);
            }
        }
        /// <summary>
        /// 客户端是否处于连接状态
        /// </summary>
        private bool Connected
        {
            get
            {
                return _socket != null && _socket.Connected;
            }
        }

        #region Implementation of ISocketClient

        /// <summary>
        /// 提交请求
        /// </summary>
        /// <param name="requestMsg"></param>
        /// <returns></returns>
        public string CommitRequest(string requestMsg)
        {
            if (!Connected)
            {
                try
                {
                    if (_socket != null)
                    { _socket.Close(); }
                    Connect();
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                    throw;
                }
            }
            lock (this)
            {
                try
                {
                    Send(requestMsg);
                    return Read();
                }
                catch (Exception e)
                {
                    bool isIOException = e is IOException;
                    if (!isIOException)
                    {
                        _logger.Info(string.Concat("非IO异常原因，SocketClient主动关闭连接：", e));
                    }
                    try
                    {
                        _socket.Close();
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(ex);
                    }
                    throw;
                }
            }
        }

        /// <summary>
        /// 关闭客户端
        /// </summary>
        public void Close()
        {
            if (_networkStream != null)
            {
                _networkStream.Close();
            }
            if (_socket != null)
            { _socket.Close(); }
            //_isStart = false;
        }

        #endregion
    }

    public class SocketClientOfLotmachine2 : ISocketClient
    {
        public SocketClientOfLotmachine2(string host, int thePort, int receiveTimeout, int sendTimeout, ILog logger)
		{			
			this.serverIP = host ;
			this.port = thePort ;
            _receiveTimeout = receiveTimeout;
            _sendTimeout = sendTimeout;
            _logger = logger;
            _charset = Encoding.UTF8;
		}
        TcpClient client = null;
        private int _receiveTimeout;
        private int _sendTimeout;
        private ILog _logger;
        private Encoding _charset;
        private NetworkStream _networkStream;
        //最后交易时间
        private volatile int _lastTranslation;
        #region ServerIP
        private string serverIP = "";
        public string ServerIP
        {
            get
            {
                return this.serverIP;
            }
            set
            {
                this.serverIP = value;
            }
        }
        #endregion

        #region Port
        private int port = 0;
        public int Port
        {
            get
            {
                return this.port;
            }
            set
            {
                this.port = value;
            }
        }
        #endregion
        #region Implementation of ISocketClient

        /// <summary>
        /// 提交请求
        /// </summary>
        /// <param name="requestMsg"></param>
        /// <returns></returns>
        public string CommitRequest(string requestMsg)
        {
            if (!Connected)
            {
                try
                {
                    if (client != null)
                    { client.Close(); }
                    Connect();
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                    throw;
                }
            }
            lock (this)
            {
                try
                {
                    Send(requestMsg);
                    return Read();
                }
                catch (Exception e)
                {
                    bool isIOException = e is IOException;
                    if (!isIOException)
                    {
                        _logger.Info(string.Concat("非IO异常原因，SocketClient主动关闭连接：", e));
                    }
                    try
                    {
                        client.Close();
                    }
                    catch (Exception ex)
                    {
                        _logger.Error(ex);
                    }
                    throw;
                }
            }
        }

        /// <summary>
        /// 关闭客户端
        /// </summary>
        public void Close()
        {
            if (this.client != null)
            {
                this.client.Close();
                this.client = null;
            }
        }

        #endregion

        /// <summary>
        /// 客户端是否处于连接状态
        /// </summary>
        private bool Connected
        {
            get
            {
                return client != null && client.Connected;
            }
        }
        
        /// <summary>
        /// 连接到远程服务器
        /// </summary>
        private void Connect()
        {
            if(client==null)
            client = new TcpClient();
            if(!client.Connected)
            client.Connect(IPAddress.Parse(this.serverIP), this.port);
            client.ReceiveTimeout = _receiveTimeout;
            client.SendTimeout = _sendTimeout;
            client.NoDelay = true;
            _networkStream = client.GetStream();
        }

        /// <summary>
        /// 发送消息包
        /// </summary>
        /// <param name="message"></param>
        private void Send(string message)
        {
            byte[] contentData = _charset.GetBytes(message);
            //因为BitConverter.GetBytes是按小端字节序转换成byte数组，而网络字节序是大端字节序，所以进行转换
            byte[] headerData = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(contentData.Length));
            _networkStream.Write(headerData, 0, headerData.Length);
            _networkStream.Write(contentData, 0, contentData.Length);
            _networkStream.Flush();
        }
        /// <summary>
        /// 读取回复消息包
        /// </summary>
        /// <returns></returns>
        private string Read()
        {
            string content = string.Empty;
            byte[] buffer = new byte[4];
            DateTime startTime = DateTime.Now;
            _logger.Info("开始接收……");
            try
            {
                _networkStream.Read(buffer, 0, buffer.Length);
                //client.Client.Receive(buffer, 0, 4, SocketFlags.None);
            }
            catch (Exception exception)
            {
                DateTime endTime = DateTime.Now;
                TimeSpan timeSpan = endTime.Subtract(startTime);
                _logger.Warn(string.Concat(exception, " 耗时：", timeSpan.TotalSeconds, " start time:", startTime.ToString("yyyy-MM-dd HH:mm:ss"), " end time:", endTime.ToString("yyyy-MM-dd HH:mm:ss")));
                return "";
            }
            
            //因为BitConverter.GetBytes是按小端字节序转换成byte数组，而网络字节序是大端字节序，所以进行转换
            int contentLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(buffer, 0));
            if (contentLength > 0)
            {
                byte[] contentData = new byte[contentLength];

                int offset = 0;
                int num = 0;
                do
                {
                    num = _networkStream.Read(contentData, offset, contentLength - offset);
                    offset += num;
                } while (num > 0 && offset < contentLength);
                if (offset != contentLength)
                {
                    throw new Exception("交易包长度与实际交易内容长度不符");
                }
                _lastTranslation = Environment.TickCount;
                content = _charset.GetString(contentData);
            }
            return content;
        }
    }
}
