using Apewer;
using Apewer.Internals;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Apewer.Network
{

    /// <summary>TCP 客户端。</summary>
    public class TcpClient
    {

        #region event

        /// <summary>Exception。</summary>
        public event Event<Exception> Excepted;

        /// <summary>已发送数据。</summary>
        public event Event<byte[]> Sent;

        /// <summary>已接收数据。</summary>
        public event Event<byte[]> Received;

        /// <summary>已连接。</summary>
        public event Event Connected;

        /// <summary>已断开。</summary>
        public event Event Closed;

        #region raise

        private void RaiseConnected() { if (Connected != null) Connected(this, new EventArgs()); }

        private void RaiseClosed() { if (Closed != null) Closed(this, new EventArgs()); }

        private void RaiseSent(byte[] value) { if (Sent != null) Sent(this, value); }

        private void RaiseReceived(byte[] value) { if (Received != null) Received(this, value); }

        private void RaiseExcepted(Exception value) { if (Excepted != null) Excepted(this, value); }

        #endregion

        #endregion

        #region definition

        private Thread _listener = null;
        private Thread _provider = null;
        private Socket _socket = null;
        private Queue<byte[]> _queue = null;

        // private AutoResetEvent _are = null;

        private bool _break = false;
        private int _timeout = 1000;

        private string _localip, _remoteip;
        private int _localport = 0, _remoteport = 0;

        private bool _background = true;

        /// <summary>构造函数。</summary>
        public TcpClient()
        {
            RemoteIP = "127.0.0.1";
            RemotePort = 0;
            Timeout = 1000;
        }

        /// <summary>构造函数。</summary>
        public TcpClient(string ip, int port, int timeout = 1000)
        {
            RemoteIP = ip;
            RemotePort = port;
            Timeout = timeout;
        }

        #endregion

        #region accessor

        /// <summary>获取或设置监听线程是否为后台线程,默认为“是”。</summary>
        public bool Background
        {
            get { return _background; }
            set
            {
                _background = value;
                try { if (_listener != null) _listener.IsBackground = value; }
                catch (Exception argex) { RaiseExcepted(argex); }
            }
        }

        /// <summary>获取或设置远程计算机的 IP 地址。</summary>
        public string RemoteIP
        {
            get { return _remoteip; }
            set
            {
                string vip = value;
                if (!NetworkUtility.IsIP(vip)) vip = NetworkUtility.Resolve(vip);
                if (!NetworkUtility.IsIP(vip)) vip = "127.0.0.1";
                _remoteip = vip;
            }
        }

        /// <summary>获取或设置远程计算机的 TCP 端口号。</summary>
        public int RemotePort
        {
            get { return _remoteport; }
            set
            {
                int vport = value;
                if (vport < 0) vport = 0;
                if (vport > 65535) vport = 65535;
                _remoteport = vport;
            }
        }

        /// <summary>获取或设置本地计算机的 IP 地址。</summary>
        public string LocalIP
        {
            get { return _localip; }
            private set { _localip = string.IsNullOrEmpty(value) ? "" : value; }
        }

        /// <summary>获取或设置本地计算机的 TCP 端口号。</summary>
        public int LocalPort
        {
            get { return _localport; }
            private set
            {
                int vport = value;
                if (vport < 0) vport = 0;
                if (vport > 65535) vport = 65535;
                _localport = vport;
            }
        }

        /// <summary>获取或设置超时时间。</summary>
        public int Timeout
        {
            get { return _timeout; }
            set { _timeout = (value > 0) ? value : 1; }
        }

        /// <summary>开始连接,并初始化发送队列。</summary>
        public void Start()
        {
            Close(false);
            _queue = new Queue<byte[]>();

            _provider = new Thread(Provider);
            _provider.IsBackground = true;
            _provider.Start();
            // _are.WaitOne(1000);
        }

        /// <summary>断开连接。</summary>
        /// <param name="event">是否引导 Closed 事件。</param>
        public void Close(bool @event = true)
        {
            CloseThread(ref _provider);
            CloseThread(ref _listener);
            CloseSocket();
            CloseQueue();
            _queue = null;
            // _are = null;
            if (@event) RaiseClosed();
        }

        /// <summary>向服务端发送数据。</summary>
        /// <param name="bytes">字节数组。</param>
        public bool Send(byte[] bytes)
        {
            if (_socket != null)
            {
                _queue.Enqueue(bytes);
                return true;
            }
            else
            {
                var vse = new SocketException(10057);
                RaiseExcepted(vse);
                return false;
            }
        }

        /// <summary>是否已连接。</summary>
        public bool Online
        {
            get
            {
                try { if (_socket != null) return _socket.Connected; }
                catch (Exception argex) { RaiseExcepted(argex); }
                return false;
            }
        }

        #endregion

        #region logic

        private void Listener()
        {
            var vb = new byte[TcpBuffer.Size];
            var vf = 0;
            while (true)
            {
                try
                {
                    if (_socket.Poll(50, SelectMode.SelectWrite))
                    {
                        _socket.Blocking = true;
                        vf = _socket.Receive(vb);
                        if (vf > 0)
                        {
                            var vms = new MemoryStream(vb);
                            vms.SetLength(vf);
                            RaiseReceived(vms.ToArray());
                            vms.Dispose();
                        }
                        else
                        {
                            if (_socket != null) break;
                        }
                    }
                }
                catch (SocketException argEx)
                {
                    if (argEx.ErrorCode != 10060) RaiseExcepted(argEx);
                    if (argEx.ErrorCode == 10054) break;
                }
                catch (Exception argEx)
                {
                    RaiseExcepted(argEx);
                    // break;
                }
            }
            if (_provider != null)
            {
                if (_provider.IsAlive) _provider.Abort();
                _provider = null;
            }
            if (_socket != null)
            {
                _socket.Disconnect(true);
                _socket.Close();
                _socket = null;
            }
            CloseThread(ref _provider);
            CloseSocket();
            CloseQueue();
            RaiseClosed();
        }

        private void Provider()
        {
            _break = false;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.SendTimeout = _timeout;
            _socket.ReceiveTimeout = _timeout;
            //_are = new AutoResetEvent(false);

            try
            {
                _socket.Blocking = true;
                _socket.Connect(_remoteip, _remoteport);
                if (_socket.Connected)
                {
                    _socket.SendBufferSize = TcpBuffer.Size;
                    _socket.ReceiveBufferSize = TcpBuffer.Size;
                    var vrep = (IPEndPoint)_socket.RemoteEndPoint;
                    var vlep = (IPEndPoint)_socket.LocalEndPoint;
                    _remoteip = vrep.Address.ToString();
                    _localip = vlep.Address.ToString();
                    _localport = vlep.Port;

                    RaiseConnected();
                    _listener = new Thread(Listener);
                    _listener.Start();
                    //  _are.Set();
                }
                else
                {
                    Close();
                    return;
                }
            }
            catch (Exception argEx)
            {
                RaiseExcepted(argEx);
            }

            while ((!_break) && (_socket != null))
            {

                if (_queue.Count > 0)
                {
                    var vb = _queue.Dequeue();
                    if (vb.Length > 0)
                    {
                        try
                        {
                            _socket.Send(vb);
                            RaiseSent(vb);
                        }
                        catch (Exception argEx)
                        {
                            RaiseExcepted(argEx);
                        }
                    }
                    else
                    {
                        Thread.Sleep(1);
                    }
                }
            }
        }

        private void CloseSocket()
        {
            if (_socket != null)
            {
                try
                {
                    _socket.Disconnect(false);
                    _socket.Close();
                }
                catch (SocketException argEx) { RaiseExcepted(argEx); }
                catch (Exception argEx) { RaiseExcepted(argEx); }
                _socket = null;
            }
        }

        private void CloseThread(ref Thread thread)
        {
            if (thread != null)
            {
                if (thread.IsAlive) thread.Abort();
                thread = null;
            }
        }

        private void CloseQueue()
        {
            if (_queue != null) _queue.Clear();
        }

        #endregion

    }
}