using System;
using System.Net;
using System.Text;
using System.Threading;
using Inspectron.HawkEye.UDPB;
using Newtonsoft.Json;

namespace Inspectron.HawkEye.Protocol
{
    public class ImageClient : IDisposable
    {
        private static readonly object connectionLock = new object();
        private readonly IPEndPoint _endpoint;
        private readonly IPAddress _adapter;

        private readonly UDPBSocket _imageSocket = new UDPBSocket();
        private readonly UDPBSocket _commandSocket = new UDPBSocket();
        private bool _isConnected = true;

        public ImageClient(IPEndPoint endpoint,IPAddress adapter)
        {
            _endpoint = endpoint;
            _adapter = adapter;
 
        }

        public void Dispose()
        {
            _isConnected = false;
            _commandSocket.Dispose();
        }

        public void Connect()
        {
            lock (connectionLock)
            {
                _commandSocket.Connect(_endpoint,_adapter);
                var port = UDPBSocket.FindFreePort(_adapter);
                var bytesPort = BitConverter.GetBytes(port);
                _commandSocket.SendData(new[]
                    {(byte) ECommand.Connect, bytesPort[0], bytesPort[1], bytesPort[2], bytesPort[3]});
                var settingsData0 = _commandSocket.Receive();
                var b = new byte[settingsData0.Length - 1];
                Array.Copy(settingsData0, 1, b, 0, b.Length);
                var settingsString = Encoding.UTF8.GetString(b);
                SettingsReceived(JsonConvert.DeserializeObject<CameraSettings>(settingsString));
                _imageSocket.Listen(_adapter,port);
                var th = new Thread(ReceiveLoop);
                th.Start();
            }
        }

        public event Action<byte[]> ImageReceived = delegate { };
        public event Action<CameraSettings> SettingsReceived = delegate { };


        public void Trigger()
        {
            _commandSocket.SendData(new[] {(byte) ECommand.Trigger});
        }

        public void ApplySettings(CameraSettings cameraSettings)
        {
            var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cameraSettings));
            var databytes = new byte[1000];
            databytes[0] = (byte) ECommand.Settings;
            Array.Copy(data, 0, databytes, 1, data.Length);

            _commandSocket.SendData(databytes);
        }

        public void StartContinuous()
        {
            _commandSocket.SendData(new[] {(byte) ECommand.StartContinuous});
        }

        public void StopContinuous()
        {
            _commandSocket.SendData(new[] {(byte) ECommand.StopContinuous});
        }

        public void SaveSettingsOnCamera()
        {
            _commandSocket.SendData(new[] {(byte) ECommand.SaveSettings});
        }


        private void ReceiveLoop()
        {
            while (_isConnected)
            {
                var data = _imageSocket.Receive();

                Process(data);

                
            }
        }

        private void Process(byte[] data)
        {
            switch ((EData) data[0])
            {
                case EData.Image:
                    var b = new byte[data.Length - 1];
                    Array.Copy(data, 1, b, 0, b.Length);
                    ImageReceived(data);
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
    }
}