using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Diagnostics;

namespace RemoteCamClient
{
    public class XBee
    {
        private SerialPort port;
        private byte frameId = 0;

        /// <summary>
        /// Initializes a new instance of the XBee class.
        /// </summary>
        /// <param name="port"></param>
        public XBee(SerialPort port)
        {
            this.port = port;
        }

        private byte GetNextFrameId()
        {
            if (frameId == 255)
                frameId = 1;
            else
                frameId++;
            return frameId;
        }

        public byte[] ReceiveBinary(out byte strength)
        {
            byte[] frame = ReceiveFrame();
            Debug.Assert(frame[0] == 0x81); //RX 16bit
            strength = frame[3];
            byte[] data = new byte[frame.Length - 5];
            Array.Copy(frame, 5, data, 0, data.Length);
            return data;
        }

        private byte[] ReceiveFrame()
        {
            byte[] frameHeader = ReceiveBytes(3);
            int dataLength = frameHeader[2];
            byte[] data = ReceiveBytes(dataLength);
            byte[] crc = ReceiveBytes(1);
            return data;
        }

        public byte[] ReceiveBytes(int length)
        {
            byte[] buffer = new byte[length];
            int total = 0;
            while (total < buffer.Length)
            {
                int read = port.Read(buffer, total, length - total);
                total += read;
            }
            return buffer;
        }

        public void SendData(byte[] data)
        {
            byte[] frame = CreateTransmitFrame(data, 0, data.Length);
            SendFrame(frame, 0, frame.Length);
        }

        private void SendFrame(byte[] frame, int start, int length)
        {
            bool done = false;
            while (!done)
            {
                port.Write(frame, 0, frame.Length);
                try
                {
                    ReceiveFrame();
                    // should check the result etc.
                    done = true;
                }
                catch (Exception ex)
                {
                    Debug.Print("Failed because " + ex.Message);
                }
            }
            Debug.Print("Done chunk");
        }


        private byte[] CreateTransmitFrame(byte[] data, int start, int length)
        {
            byte[] frameData = new byte[length + 5];
            frameData[0] = 0x01; // TX
            frameData[1] = GetNextFrameId();  // Frame ID
            frameData[2] = 0xFF; // Address MSB
            frameData[3] = 0xFF; // Address LSB
            frameData[4] = 0x00;  // options
            Array.Copy(data, start, frameData, 5, length);

            byte[] result = new byte[frameData.Length + 4]; //
            // start delimiter
            result[0] = 0x7E;
            // length MSB, LSB
            result[1] = 0; // always sending less than 256 bytes
            result[2] = (byte)frameData.Length;
            Array.Copy(frameData, 0, result, 3, frameData.Length);
            result[result.Length - 1] = CalculateChecksum(frameData);
            return result;
        }

        private byte CalculateChecksum(byte[] data)
        {
            int checksum = data[0];
            for (int i = 1; i < data.Length; i++)
            {
                checksum += data[i];
                checksum &= 0xFF;
            }
            return (byte)(0xFF - (byte)checksum);
        }
    }

}
