
#include "ParsedStream.h"


void ParsedStream::storeByte(unsigned char c) {
  int i = (_rx_buffer.head + 1) % RX_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != _rx_buffer.tail) {
    _rx_buffer.buffer[_rx_buffer.head] = c;
    _rx_buffer.head = i;
  }
}

ParsedStream::ParsedStream()
{
  /*
   */
  reset();
}

void ParsedStream::begin(Stream* theUart)
{
  _uart = theUart;
}

void ParsedStream::reset() {
  /*
   */
  ring_buffer _rx_buffer = { { 0 }, 0, 0};
  _closed = false;
  bytes_matched = 0;  
}


uint8_t ParsedStream::available(bool raw) {
  uint8_t available_bytes;
  
  available_bytes = (RX_BUFFER_SIZE + _rx_buffer.head - _rx_buffer.tail) % RX_BUFFER_SIZE;

  if (!raw) {
    if (available_bytes > bytes_matched) {
      available_bytes -= bytes_matched;
    } else {
      available_bytes = 0;
    }
  }

  return available_bytes;
}

uint8_t ParsedStream::available() {

  // NOTE: This causes a read/buffer fill which isn't entirely
  //       consistent with how `available()` is normally
  //       handled.
  //       TODO: Put this buffer fill in the read section instead?
  
  // TODO: Don't refill if we're almost full and don't have a partial
  //       match?

  while (!_closed && freeSpace() && _uart->available()) {
    getByte();
  }
  return available(false);
}

bool ParsedStream::closed() {
  return _closed && !available();
}

int ParsedStream::read(void) {

  if (!available()) {
    getByte();
  }

  if (!available()) {
    return -1;
  } else {
    unsigned char c = _rx_buffer.buffer[_rx_buffer.tail];
    _rx_buffer.tail = (_rx_buffer.tail + 1) % RX_BUFFER_SIZE;
    return c;
  }
}

int ParsedStream::peek(void) {

  if (!available()) {
    getByte();
  }

  if (!available()) {
    return -1;
  } else {
    unsigned char c = _rx_buffer.buffer[_rx_buffer.tail];
    return c;
  }
}



int ParsedStream::freeSpace() {
  return RX_BUFFER_SIZE - available(true) - 1 /* The -1 fudge due to storeByte calculation*/;
}

void ParsedStream::getByte() {
  int c;
  if (_closed) {
    return;
  }

  if (freeSpace() == 0) {
    return;
  }
  
  // TODO: Tidy this...
  c = _uart->read();
  if (c == -1) {
    return;
  }

  if (c == MATCH_TOKEN[bytes_matched]) {
    bytes_matched++;
    if (bytes_matched == strlen(MATCH_TOKEN)) {
      _closed = true;
    }
  } else if (c == MATCH_TOKEN[0]) {
    // Handle e.g. case "**CLOS*"
    bytes_matched = 1;
  } else {
    bytes_matched = 0;
  }

  storeByte(c);
  
}
