using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Inspectron.HawkEye.Protocol.Interfaces;
using Inspectron.HawkEye.UDPB;
using Newtonsoft.Json;

namespace Inspectron.HawkEye.Protocol
{
    public class ImageServer
    {
        private readonly IImageSource _imageSource;
        private readonly ICameraControl _cameraControl;
        private readonly ILightControl _lightControl;
        private readonly UDPBSocket _commandSocket = new UDPBSocket();
        private readonly UDPBSocket _imageSocket = new UDPBSocket();

        private bool _autoTrigger;
        private bool _applySettings;
        private CameraSettings _imageSettingsToApply=new CameraSettings(){ImageSettings = new ImageSettings()};

        public ImageServer(IImageSource imageSource, ICameraControl cameraControl, ILightControl lightControl)
        {
            _imageSource = imageSource;
            _cameraControl = cameraControl;
            _lightControl = lightControl;
            _imageSocket.LossSimulation = 0;
        }

        public void Start()
        {
            _commandSocket.Listen(IPAddress.Any, 27001);
            var th = new Thread(ReceiveLoop);
            th.Start();
            if (File.Exists("settings.json"))
            {
                var settings = JsonConvert.DeserializeObject<CameraSettings>(File.ReadAllText("settings.json"));
                _imageSettingsToApply = settings;
                ApplyParameters(settings);
            }
        }

        public void ApplyParameters(CameraSettings cameraSettings)
        {
            _cameraControl.SetParameters(cameraSettings);

            _lightControl.SetLight(cameraSettings.LightPwm1, cameraSettings.LightPwm2);
        }

        private void SaveSettingsLocally()
        {
            File.WriteAllText("settings.json", JsonConvert.SerializeObject(_imageSettingsToApply));
        }

        private void ReceiveLoop()
        {
            while (true)
            {
                
                var data = _commandSocket.Receive();
                try
                {
                    ProcessCommand(data);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }

        private void TriggerLoop()
        {
            while (_autoTrigger)
            {

                if (_applySettings)
                {
                    ApplyParameters(_imageSettingsToApply);
                    _applySettings = false;
                }

                try
                {
                    SendImage();
                    
                }
                catch
                {
                    return;
                }
            }
        }

        private void ProcessCommand(byte[] data)
        {
            Console.WriteLine(((ECommand) data[0]).ToString());
            switch ((ECommand) data[0])
            {
                case ECommand.Connect:
                {
                    var port = new byte[4];
                    Array.Copy(data, 1, port, 0, 4);
                    _imageSocket.Connect(new IPEndPoint((_commandSocket.LastConnection as IPEndPoint).Address, BitConverter.ToInt32(port,0)),null);
                    var databytes = new byte[1000];
                    var settings = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_imageSettingsToApply));
                    Array.Copy(settings, 0, databytes, 1, settings.Length);
                    databytes[0] = (byte) ECommand.Settings;
                    _commandSocket.SendData(databytes);
                }
                    break;
                case ECommand.Trigger:
                {
                    Task.Run(() => { SendImage(); });
                }
                    break;
                case ECommand.StartContinuous:
                {
                    _autoTrigger = true;
                    var th = new Thread(TriggerLoop);
                    th.Start();
                }
                    break;
                case ECommand.StopContinuous:
                {
                    _autoTrigger = false;
                }
                    break;
                case ECommand.Settings:
                {
                    var databytes = new byte[999];
                    Array.Copy(data, 1, databytes, 0, 999);
                    var settings = JsonConvert.DeserializeObject<CameraSettings>(Encoding.UTF8.GetString(databytes));
                    _imageSettingsToApply = settings;
                    if (_autoTrigger)
                        _applySettings = true;
                    else
                        ApplyParameters(_imageSettingsToApply);
                }
                    break;
                case ECommand.SaveSettings:
                {
                    SaveSettingsLocally();
                }
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private void SendImage()
        {
            var image = _imageSource.GetImage();
            var b = new byte[image.Length + 1];
            b[0] = (byte) EData.Image;
            Array.Copy(image, 0, b, 1, image.Length);

            _imageSocket.SendData(b);
        }
    }
}