using System;
using System.IO;
using System.IO.Ports;
using System.Threading;

using Microsoft.SPOT;

namespace CPF
{
    class Uart  : IDisposable
    {

        #region Public properties

        // Used SerialPort
        public readonly String SerialPort;

        // Used Baudrate
        public readonly int Baudrate;

        #endregion

        #region Internal properties and functions

        // UART Object
        private SerialPort  _uart;

        // FIFO used for reading
        private RingBuffer  _input_FIFO;

        // Arrays used for read and writing from the UART 
        private byte[]      _readBuffer;

        // true when disposed
        private bool        _disposed = false;

        // Data recieved handler
        private void dataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            int bytesToRead;
            // Only accept char data
            if (e.EventType == SerialData.Eof)
                return;
            bytesToRead = _uart.BytesToRead;
            while (bytesToRead > 0)
            {
                _uart.Read(_readBuffer, 0, bytesToRead);
                _input_FIFO.Write(_readBuffer, 0, bytesToRead);
                bytesToRead = _uart.BytesToRead;
            }
        }

        #endregion

        #region Constructor/Destructor

        // Create new Uart class
        public Uart(String SerialPort, int Baudrate=19200)
        {
            // Store parameters
            this.SerialPort = SerialPort;
            this.Baudrate = Baudrate;

            // Create FIFOs to hold user data
            _input_FIFO = new RingBuffer(8192);

            // Create a 64 byte read buffer
            _readBuffer = new byte[256];

            // Create new UART with 8N1 and no RTS Handshaking
            _uart = new SerialPort(SerialPort, Baudrate, Parity.None, 8, StopBits.One);
            _uart.Handshake = Handshake.None;

            // Add DATA Recieved Handler
            _uart.DataReceived += new SerialDataReceivedEventHandler(dataReceived);

            // Open UART
            _uart.Open();
        }

        // Dispose Uart Interface
        public void Dispose()
        {
            try
            {
                if (_disposed == false)
                {
                    _uart.Close();
                    _uart.Dispose();
                }
            }
            finally
            {
                _disposed = true;
            }
        }

        #endregion

        // Send raw data to AL3A in DATA mode
        public void SendLine(byte[] bytes)
        {
            _uart.Write(bytes, 0, bytes.Length);
            _uart.Write(IridiumStrings.CRLF, 0, IridiumStrings.CRLF.Length);
        }

        // Send raw data to AL3A in DATA mode
        public void SendRawData(byte[] bytes, int offset, int count)
        {
            _uart.Write(bytes, offset, count);
        }

        // Send CR-LF
        public void SendNewLine()
        {
            _uart.Write(IridiumStrings.CRLF, 0, IridiumStrings.CRLF.Length);
        }

        public void SendEscape()
        {
            _uart.Write(IridiumStrings.AT_ESCAPE, 0, IridiumStrings.AT_ESCAPE.Length);
        }

        public bool Available()
        {
            bool result;
            lock (_input_FIFO)
            {
                result = (_input_FIFO.IsEmpty()) ? false : true;
            }
            return result;
        }

        public byte GetChar()
        {
            byte ch;
            lock (_input_FIFO)
            {
                ch = _input_FIFO.Read();
            }
            return ch;
        }

        public void PushBackChar(byte ch)
        {
            lock (_input_FIFO)
            {
                _input_FIFO.PushBack(ch);
            }
        }

        public void FlushIn()
        {
            lock (_input_FIFO)
            {
                _input_FIFO.Clear();
            }
        }

    }
}
