using System.Net.Sockets;
using System.Text;
using Interfaces;

namespace IrDaemonNotifier
{
    class IrDaemonCommunicator
    {
        private const string IRDAEMON_SERVER_IP = "192.168.1.65";
        private const int IRDAEMON_PORT_NO = 3006;
        private const int IRDAEMON_PROPERTY_PORT_NO = 3007;

        private readonly ILoggerService _logger;

        public IrDaemonCommunicator(ILoggerService logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// Send a command to the IrDaemon endpoint
        /// </summary>
        /// <param name="command">string command name</param>
        //TODO: convert to use an enum value perhaps? "supported command" style?
        public void SendCommand(string command)
        {
            var formattedCommand = command;//FormatCommand(command);    //seems we may not actually need any formatting..

            //---create a TCPClient object at the IP and port no.---
            var client = new TcpClient(IRDAEMON_SERVER_IP, IRDAEMON_PORT_NO);
            var nwStream = client.GetStream();
            var bytesToSend = Encoding.ASCII.GetBytes(formattedCommand);

            //---send the text---
            _logger.Trace("Sending: {0}", formattedCommand);
            nwStream.Write(bytesToSend, 0, bytesToSend.Length);

            //---read back the text---
            var bytesToRead = new byte[client.ReceiveBufferSize];
            var bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
            _logger.Trace("Received: {0}", Encoding.ASCII.GetString(bytesToRead, 0, bytesRead));
            client.Close();
        }

        /// <summary>
        /// Get data from the IrDaemon for the requested property name
        /// </summary>
        /// <typeparam name="T">type to convert property value to</typeparam>
        /// <param name="command">The name of the property to request data for</param>
        /// <returns>the object returned from the ir daemon, converted to the Type T</returns>
        public T RequestData<T>(string command)
        {
            try
            {
                var formattedCommand = FormatCommand(command);

                //---create a TCPClient object at the IP and port no.---
                var client = new TcpClient(IRDAEMON_SERVER_IP, IRDAEMON_PROPERTY_PORT_NO);
                var nwStream = client.GetStream();
                var bytesToSend = Encoding.ASCII.GetBytes(formattedCommand);

                //---send the text---
                _logger.Trace("Sending: {0}", formattedCommand);
                nwStream.Write(bytesToSend, 0, bytesToSend.Length);

                //---read back the text---
                var bytesToRead = new byte[client.ReceiveBufferSize];
                var bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
                var stringResponse = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
                client.Close();
                stringResponse = StripFormatting(stringResponse);
                _logger.Trace("Received: {0}", stringResponse);

                var strId = GetTokenIdString(stringResponse);

                try
                {
                    return Convert<T>(strId);
                }
                catch
                {
                    return default(T);
                }
            }
            catch (System.Exception ex)
            {
                _logger.Error(ex);
            }
            return default(T);
        }

        /// <summary>
        /// Format the command string for the ir daemon "protocol"
        /// </summary>
        /// <param name="command">command to format</param>
        /// <returns>formatted command</returns>
        private string FormatCommand(string command)
        {
            return string.Format("#{0}#\n", command);
        }

        /// <summary>
        /// remove the formatting from the string
        /// </summary>
        /// <param name="data">string to remove formatting from</param>
        /// <returns>string with protocol formatting removed</returns>
        private string StripFormatting(string data)
        {
            return data.Replace("#", "").Replace("\n", "");
        }

        private string GetTokenIdString(string data)
        {
            var bits = data.Split(' ');
            if(bits.Length == 3)
             return bits[1];
            return string.Empty;
        }

        private T Convert<T>(string data)
        {
            return (T)System.Convert.ChangeType(data, typeof(T));
        }
    }

    internal class Commands
    {

        public class Properties
        {
            public const string GetLastToken = "LAST_SCANNED_TOKEN";
        }

        public const string Shutdown = "SHUTDOWN#";
        public const string Reset = "RESET#";
        public const string StartNewRace = "START_NEW_RACE#";
    }
}
