using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Aitex.Sorter.Common;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.Event;
using System.Configuration;
using MECF.Framework.Common.Equipment;
using System.Threading;
namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Robot
{  
    public enum DataType
    {
        Ascii,
        Byte
    }
    public class AsyncSocket :  ICommunication, IDisposable
    {
        public delegate void ErrorHandler(ErrorEventArgs args);
        public event ErrorHandler OnErrorHappened;
        public delegate void MessageHandler(string message);
        public event MessageHandler OnDataChanged;
        public delegate void BufferDataHander(byte[] data);
        public event BufferDataHander OnBufferDataReceived;
        public delegate void ConnectHandler();
        public event ConnectHandler OnConnect;
        private static Object _locker = new Object();
        public class ClientStateObject
        {
            // Client socket.     
            public Socket workSocket = null;
            // Size of receive buffer.     
            public const int BufferSize = 8192;
            // Receive buffer.     
            public byte[] buffer = new byte[BufferSize];
            // Received data string.     
            public StringBuilder sb = new StringBuilder();
        }
        private string _newLine { get; set; }
        private Socket _socket;
        private string _ip;
        private int _port;
        private DataType _dataType;
        private bool _supportReconnect = false;
        private int _reconnectInterval = 3000;
        private object _reconnectLocker = new object();
        private Thread _reconnectThread = null;
        
        public bool IsConnected { get { return (_socket != null && _socket.Connected); } }
        public AsyncSocket(string address, string newline ="\r",DataType dataType=DataType.Ascii,bool supportReconnect=false)
        {
                   // Connect(address);
            _socket = null;
            _newLine = newline;
            _supportReconnect = supportReconnect;
            _dataType = dataType;
        }
        ~AsyncSocket()
        {
            Dispose();
        }
        public void Connect(string address)
        {
            _ip =address.Split(':')[0];
            _port =int.Parse(address.Split(':')[1]);
            try
            {
                Connect();
            }
            catch
            {
            }
        }
        /// 
        /// 连接
        /// 
        /// 
        private void Connect()
        {
            try
            {
                IPAddress ipAddress = IPAddress.Parse(_ip);
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, _port);
                
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.ReceiveTimeout = 2000;
                _socket.SendTimeout = 2000;
                // Connect to the remote endpoint.     
                _socket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), _socket);
            }
            catch (Exception e)
            {
                if (!_supportReconnect)
                {
                    throw new Exception(e.ToString());
                }
                else
                {
                    StartReconnectThread();
                }
            }
        }
        /// 
        /// 启动重新连接线程
        /// 
        private void StartReconnectThread()
        {
            lock (_reconnectLocker)
            {
                if (_reconnectThread==null)
                {
                    _reconnectThread = new Thread(ReConnectThread);
                    _reconnectThread.IsBackground = true;
                    _reconnectThread.Start();
                }
            }
        }
        /// 
        /// 重新连接线程
        /// 
        private void ReConnectThread()
        {
            //休眠一定时间后再连接
            Thread.Sleep(_reconnectInterval);
            try
            {
                Connect();
            }
            catch
            {
            }
            _reconnectThread = null;
        }
        /// 
        /// 连接回调函数
        /// 
        /// 
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.     
                Socket client = (Socket)ar.AsyncState;
                // Complete the connection.   
                client.EndConnect(ar);                    
                if(OnConnect!=null)
                {
                    OnConnect();
                }
                // Receive the response from the remote device. 
                Receive(_socket);
            }
            catch(Exception e)
            {
                string reason = string.Format("Communication  {0}:{1:D} {2}.", _ip, _port, e.Message);
                OnErrorHappened(new ErrorEventArgs(reason));
                if (_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
        }
        /// 
        /// 异步接收数据
        /// 
        /// 
        private void Receive(Socket client)
        {
            try
            {
                // Create the state object.     
                ClientStateObject state = new ClientStateObject();
                state.workSocket = client;
                // Begin receiving the data from the remote device.     
                client.BeginReceive(state.buffer, 0, ClientStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                string reason = string.Format("TCP connect failed:{0}", e.Message);
                OnErrorHappened(new ErrorEventArgs(reason)); 
                if (_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
        }
        /// 
        /// 接收数据回调数据
        /// 
        /// 
        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                if (!IsConnected) { return; }
                // Retrieve the state object and the client socket     
                // from the asynchronous state object.     
                ClientStateObject state = (ClientStateObject)ar.AsyncState;
                Socket client = state.workSocket;
                // Read data from the remote device.     
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    // There might be more data, so store the data received so far.     
                    if (_dataType == DataType.Ascii)
                    {
                        state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                        if (state.sb.Length > _newLine.Length)
                        {
                            if (state.sb.ToString().Substring(state.sb.Length - _newLine.Length).Equals(_newLine))
                            {
                                string msg = state.sb.ToString();
                                if (OnDataChanged != null)
                                {
                                    OnDataChanged(state.sb.ToString());
                                }
                                state.sb.Clear();
                            }
                        }
                    }
                    else
                    {
                        if(OnBufferDataReceived!=null)
                        {
                            OnBufferDataReceived(state.buffer);
                        }
                    }
                    // Get the rest of the data.     
                    client.BeginReceive(state.buffer, 0, ClientStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                string reason = string.Format("TCP Socket recevice data failed:{0}", ex.Message);
                OnErrorHappened(new ErrorEventArgs(reason)); 
                if (_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
        }
        /// 
        /// 写数据
        /// 
        /// 
        /// 
        public bool Write(string data)
        {
            if(_socket==null||!_socket.Connected)
            {
                return false;
            }
            try
            {
                lock (_locker)
                {
                    // Convert the string data to byte data using ASCII encoding.     
                    byte[] byteData = Encoding.ASCII.GetBytes(data);
                    _socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), _socket);
                }
                return true;
                
            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                string reason = string.Format("Send command failed:{0}", ex.Message);
                OnErrorHappened(new ErrorEventArgs(reason)); 
                if (_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
            return false;
        }
        /// 
        /// 写入二进制数据
        /// 
        /// 
        /// 
        public bool Write(byte[] byteData)
        {
            if (_socket == null || !_socket.Connected)
            {
                return false;
            }
            try
            {
                lock (_locker)
                {  
                    _socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), _socket);
                }
                return true;
            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                string reason = string.Format("Send command failed:{0}", ex.Message);
                OnErrorHappened(new ErrorEventArgs(reason));
                if(_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
            return false;
        }
        /// 
        /// 发送数据回调函数
        /// 
        /// 
        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.     
                Socket client = (Socket)ar.AsyncState;
                // Complete sending the data to the remote device.     
                int bytesSent = client.EndSend(ar);
            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                string reason = string.Format("Send command failed:{0}", ex.Message);
                OnErrorHappened(new ErrorEventArgs(reason));
                if (_supportReconnect)
                {
                    StartReconnectThread();
                }
            }
        }
        /// 
        /// 释放资源(Dispose)
        /// 
        public void Dispose()
        {
            try
            {
                if (_socket != null)
                {
                    if (IsConnected)
                    {
                        _socket.Shutdown(SocketShutdown.Both);
                    }
                    _socket.Close();
                    _socket.Dispose();
                    _socket = null;
                    _supportReconnect = false;
                }
            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                string reason = string.Format("释放socket资源失败:{0}", ex.Message);
                OnErrorHappened(new ErrorEventArgs(reason));
            }
        }
    }
}