using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace EPD.Data
{
    public class SocketClientWrapper : IDisposable
    {
        public Action OnConnected;
        public Action OnDisconnected;
        public Action OnDataChanged;
        private static Object _lockerSender = new Object();
        private BlockingCollection SetPointCommandQueue = new BlockingCollection();
        Timer timer = new Timer(500);
        public class ClientStateObject
        {
            public const int BufferSize = 16384;
            public Socket WorkSocket = null;
            public byte[] CacheBufferByteData = new byte[BufferSize];
        }
        private Socket socket;
        public bool IsConnected { get { return socket != null && socket.Connected; } }
        public SocketClientWrapper()
        {
            timer.Elapsed += Timer_Elapsed;
            timer.Enabled = true;
        }
        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (SetPointCommandQueue.Count > 0)
            {
                var byteData = SetPointCommandQueue.Take();
                socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), socket);
            }
        }
        ~SocketClientWrapper()
        {
            Dispose();
        }
        /// 
        /// 
        /// 
        /// 127.0.0.1:15000
        public void Connect(string ip, int port)
        {
            try
            {
                var remote = new IPEndPoint(IPAddress.Parse(ip), port);
                Dispose();
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.BeginConnect(remote, ConnectCallback, socket);
                //Task.Run(() => 
                //{
                //    while (SetPointCommandQueue.Count > 0)
                //    {
                        
                //        System.Threading.Thread.Sleep(500);
                //    }
                //    System.Threading.Thread.Sleep(500);
                //});
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error in Connect, " + ex.Message);
            }
        }
        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                var client = (Socket)ar.AsyncState;
                client.EndConnect(ar);
                if (OnConnected != null)
                    OnConnected();
                System.Diagnostics.Trace.Write("Connected");
                var state = new ClientStateObject();
                state.WorkSocket = socket;
                socket.BeginReceive(state.CacheBufferByteData, 0, ClientStateObject.BufferSize, 0, ReceiveCallback, state);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error in ConnectCallback, " + ex.Message);
            }
        }
        public void Disconnect()
        {
            Dispose();
            if (OnDisconnected != null)
                OnDisconnected();
        }
        private void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                if (!IsConnected)
                    return;
                ClientStateObject state = (ClientStateObject)ar.AsyncState;
                Socket client = state.WorkSocket;
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    if (OnDataChanged != null)
                        OnDataChanged(state.CacheBufferByteData, 0, bytesRead);
                    client.BeginReceive(state.CacheBufferByteData, 0, ClientStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error in ReceiveCallback, " + ex.Message);
                Disconnect();
            }
        }
        public bool Send(byte[] byteData)
        {
            try
            {
                if (!IsConnected)
                    return false;
                lock (_lockerSender)
                {
                    SetPointCommandQueue.Add(byteData);
                    //socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), socket);
                }
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error int Write, " + ex.Message);
            }
            return false;
        }
        private void SendCallback(IAsyncResult ar)
        {
            try
            {
                lock (_lockerSender)
                {
                    Socket client = (Socket)ar.AsyncState;
                    client.EndSend(ar);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error in SendCallback, " + ex.Message);
            }
        }
        public void Dispose()
        {
            try
            {
                if (socket != null)
                {
                    if (IsConnected)
                    {
                        socket.Shutdown(SocketShutdown.Both);
                    }
                    socket.Close();
                    socket.Dispose();
                    socket = null;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.Write("Error in Dispose, " + ex.Message);
            }
        }
    }
}