using System;
using Microsoft.SPOT;
using System.IO.Ports;
using SecretLabs.NETMF.Hardware.Netduino;
using System.Text;
using System.Threading;
using Microsoft.SPOT.Hardware;

namespace RemoteCam.Server
{
    public class Worker: IDisposable
    {
        private readonly XBee xbee;
        private readonly SerialPort cameraPort;
        private readonly C328R camera;
        private readonly OutputPort led;
        private readonly OutputPort cameraReset;

        public bool IsDone { get; private set; }
        private readonly byte[] buffer = new byte[255];
        public bool SnapshotRequired { get; private set; }

        public Worker()
        {
            led = new OutputPort(Pins.ONBOARD_LED, false);
            SerialPort xbeePort = new SerialPort(SerialPorts.COM1, 38400) { ReadTimeout = 10000 };
            xbee = new XBee(xbeePort);
            cameraPort = new SerialPort(SerialPorts.COM2, 115200);
            cameraReset = new OutputPort(Pins.GPIO_PIN_D7, false);
            // camera opens the port internally 
            camera = new C328R(cameraPort);
        }

        public void Start()
        {
            led.Write(true);
            xbee.DataReceived += xbee_DataReceived;
            xbee.Start();

            SyncCamera();
            InitCamera();
            led.Write(false);

            // test
            //byte[] dat = new byte[100];
            //xbee.Send(dat, 0, dat.Length);
            // TestSend();
        }

        void xbee_DataReceived(object sender, EventArgs e)
        {
            byte[] data = xbee.ReceiveFrame();
            for (int i = 5; i < data.Length; i++)
            {
                if (data[i] == 66)
                {
                    Debug.Print("Command acquired");
                    TakeSnapshotAndSendItAdvanced();
                    break;
                }
            }
        }

        private void SyncCamera()
        {
            int pass = 0;
            while (!camera.Sync())
            // while (true)
            {
                Debug.Print("Reseting");
                led.Write(false);
                TurnOffCamera();
                Thread.Sleep(100);
                led.Write(true);
                Thread.Sleep(1000);
                TurnOnCamera();
                pass++;
            }
            Debug.Print("Camera sync done at pass " + pass);
        }

        private void InitCamera()
        {
            bool result = camera.Initial(C328R.ColorType.Jpeg, C328R.PreviewResolution.R160x120, C328R.JpegResolution.R640x480);
            Debug.Assert(result);
        }

        private void TurnOffCamera()
        {
            cameraReset.Write(true);
        }

        private void TurnOnCamera()
        {
            cameraReset.Write(false);
        }

        public void TakeSnapshotAndSendIt()
        {
            SnapshotRequired = false;
            byte[] buffer = null;

            SyncCamera();
            bool result = camera.GetJpegPicture(C328R.PictureType.Jpeg, out buffer, 3000);
            if (!result)
            {
                Debug.Print("GetJpegPicture failed");
                return;
            }
            Debug.Print("Picture acquired of size " + buffer.Length);
            byte[] lengthVar = BitConverter.GetBytes(buffer.Length);
            Debug.Print(lengthVar[0] + ":" + lengthVar[1] + ":" + lengthVar[2] + ":" + lengthVar[3]);
            xbee.Send(lengthVar, 0, lengthVar.Length);
            xbee.Send(buffer, 0, buffer.Length);
            Debug.Print("Data sent");
        }

        public void TakeSnapshotAndSendItAdvanced()
        {
            SyncCamera();

            bool result = camera.GetJpegPictureInPieces(C328R.PictureType.Jpeg, 3000, 
                delegate (int pictureSize) 
                {
                    byte[] lengthVar = BitConverter.GetBytes(pictureSize);
                    xbee.Send(lengthVar, 0, lengthVar.Length);
                },
                delegate (byte[] data, int start, int length)
                {
                    xbee.Send(data, start, length);
                });
        }

        private void TestSend()
        {
            for (int length = 1; length < 11; length++)
            {
                Debug.Print("** LENGTH " + length);
                byte[] data = new byte[length];
                for (int i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)i;
                }
                for (int retry = 0; retry < 10; retry++)
                {
                    Debug.Print("**** RETRY " + retry + " of " + length);
                    xbee.Send(data, 0, data.Length);
                    Thread.Sleep(50);
                }
            }
        }

        public void Dispose()
        {
            led.Dispose();
            cameraPort.Dispose();
            xbee.DataReceived -= xbee_DataReceived;
            xbee.Dispose();
        }
    }
}
