using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using log4net;

namespace VIZ.Framework.Connection
{
    /// <summary>
    /// TCP终结点管理器
    /// </summary>
    public class TcpEndpointManager : IDisposable
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(TcpEndpointManager));

        /// <summary>
        /// 接收数据数组大小
        /// </summary>
        public const int RECV_BUFFER_SIZE = 1024 * 10;

        /// <summary>
        /// 接收数据线程间隔
        /// </summary>
        public const int RECV_THREAD_SLEEP = 10;

        /// <summary>
        /// TCP终结点管理器
        /// </summary>
        /// <param name="key">网络终结点键</param>
        /// <param name="ip">IP</param>
        /// <param name="port">端口</param>
        public TcpEndpointManager(string key)
        {
            this.Key = key;
        }

        /// <summary>
        /// 网络终结点键
        /// </summary>
        public string Key { get; private set; }

        /// <summary>
        /// 监听IP
        /// </summary>
        public string LocalIP { get; private set; }

        /// <summary>
        /// 监听端口
        /// </summary>
        public int LocalPort { get; private set; }

        /// <summary>
        /// 远程IP
        /// </summary>
        public string RemoteIP { get; private set; }

        /// <summary>
        /// 远程端口
        /// </summary>
        public int RemotePort { get; private set; }

        /// <summary>
        /// TCP客户端
        /// </summary>
        public TcpClient TcpClient { get; private set; }

        /// <summary>
        /// TCP流对象
        /// </summary>
        public NetworkStream NetworkStream { get; private set; }

        /// <summary>
        /// TCP连接
        /// </summary>
        public TcpConnection TcpConnection { get; internal set; }

        /// <summary>
        /// 包处理器
        /// </summary>
        public IConnPackageProvider PackageProvider { get; set; }

        /// <summary>
        /// 锁对象
        /// </summary>
        private object lock_object = new object();

        /// <summary>
        /// 消息接收线程
        /// </summary>
        private Thread recvThread;

        /// <summary>
        /// 接收线程信息
        /// </summary>
        private ConnThreadInfo recvThreadInfo;

        /// <summary>
        /// 连接
        /// </summary>
        /// <param name="remoteIP">远程IP</param>
        /// <param name="remotePort">远程端口</param>
        public void Connect(string remoteIP, int remotePort)
        {
            this.RemoteIP = remoteIP;
            this.RemotePort = remotePort;

            this.TcpClient = new TcpClient();
            this.TcpClient.Connect(IPAddress.Parse(remoteIP), remotePort);

            this.LocalIP = this.TcpClient.Client.LocalEndPoint.ToString();

            this.NetworkStream = this.TcpClient.GetStream();
        }

        /// <summary>
        /// 启动消息接收
        /// </summary>
        public void StartRecvMessage()
        {
            if (this.recvThreadInfo != null)
                return;

            this.recvThreadInfo = new ConnThreadInfo();
            this.recvThread = new Thread(this.executeRecvThread);
            this.recvThread.Start();
        }

        /// <summary>
        /// 停止消息接收
        /// </summary>
        public void StopRecvMessage()
        {
            this.recvThreadInfo.IsCancel = true;
            this.recvThread = null;
            this.recvThreadInfo = null;
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            this.StopRecvMessage();

            this.NetworkStream?.Close();
            this.NetworkStream?.Dispose();
            this.TcpClient?.Close();
            this.TcpClient?.Dispose();
            this.TcpClient = null;
            this.NetworkStream = null;

            this.LocalIP = null;
            this.LocalPort = 0;
            this.RemoteIP = null;
            this.RemotePort = 0;
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            try
            {
                this.Close();
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="buffer"></param>
        public void Send(byte[] buffer)
        {
            if (this.TcpClient == null || !this.TcpClient.Connected)
                throw new Exception("tcp conneection is not ready.");

            lock (this.lock_object)
            {
                this.NetworkStream.Write(buffer, 0, buffer.Length);
                this.NetworkStream.Flush();
            }
        }

        /// <summary>
        /// 执行消息接收
        /// </summary>
        private void executeRecvThread()
        {
            ConnThreadInfo info = this.recvThreadInfo;
            if (info == null)
                return;

            while (!info.IsCancel)
            {
                Thread.Sleep(RECV_THREAD_SLEEP);

                // 是否拥有数据
                if (!this.NetworkStream.DataAvailable)
                {
                    continue;
                }

                byte[] buffer = new byte[RECV_BUFFER_SIZE];
                int read = 0;
                lock (this.lock_object)
                {
                    read = this.NetworkStream.Read(buffer, 0, RECV_BUFFER_SIZE);
                }
                // 未读取到数据
                if (read <= 0)
                {
                    continue;
                }

                if (this.PackageProvider == null)
                {
                    continue;
                }

                // 处理数据包
                ConnPackageInfo package = new ConnPackageInfo();
                package.LocalIP = this.LocalIP;
                package.LocalPort = this.LocalPort;
                package.RemoteIP = this.RemoteIP;
                package.RemotePort = this.RemotePort;
                package.Data = buffer;
                package.DataSize = read;

                try
                {
                    this.PackageProvider.Execute(package);
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }
            }
        }
    }
}
