/*
   Copyright (C) 2004 T. Scott Dattalo

This file is part of gpsim.

gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */


#include "protocol.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef putc
#undef putc
#endif

unsigned int a2i(char b)
{

  if( b>='0'  && b<='9')
    return b-'0';

  if( b>='A' && b<='F')
    return b-'A'+10;

  if( b>='a' && b<='f')
    return b-'a'+10;

  return 0;

}
char i2a(unsigned int i)
{
  i &= 0x0f;
  if(i < 10)
    return '0' + i;

  return 'A'+ i- 10;
}

unsigned int ascii2uint(char **buffer, int digits)
{

  unsigned int ret = 0;
  char *b = *buffer;

  for(int i=0; i<digits; i++)
    ret = (ret << 4) + a2i(*b++);

  *buffer = b;

  return ret;
}


static unsigned int ascii2uint64(char *buffer, int digits)
{

  unsigned int ret = 0;
  char *b = buffer;

  for(int i=0; i<digits; i++)
    ret = (ret << 4) + a2i(*b++);

  return ret;
}

static unsigned int ascii2uint(char *buffer, int digits)
{

  unsigned long long i = ascii2uint64(buffer,digits);

  return (unsigned int) i;
}

//========================================================================

PacketBuffer::PacketBuffer(unsigned int _size)
  : size(_size)
{
  buffer = new char[_size];
  index = 0;
}

PacketBuffer::~PacketBuffer()
{
  delete [] buffer;
}


void PacketBuffer::puts(const char *s, int len)
{
  if(!s || len <= 0)
    return;

  unsigned int ulen = len;
  if(ulen > (size - index)) {
    ulen = size-index;
  }

  if(ulen) {
    memcpy(&buffer[index],s, ulen);
    index += ulen;
  }
}

void PacketBuffer::advanceIndex(unsigned int amount)
{
  if(index + amount <  size)
    index += amount;
  else
    index = size-1;
}

void PacketBuffer::terminate()
{
  if(index < size)
    buffer[index]=0;
}
//========================================================================
Packet::Packet(unsigned int rxsize, unsigned int txsize)
{
  rxBuffer = new PacketBuffer(rxsize);
  txBuffer = new PacketBuffer(txsize);
}

bool Packet::DecodeHeader()
{
  
  if(*rxBuffer->buffer == '$') {
    rxBuffer->index = 1;
    return true;
  }

  rxBuffer->index = 0;

  return false;
}

bool Packet::DecodeChar(char c)
{
  if(*rxBuffer->getBuffer() == c) {
    rxBuffer->index++;
    return true;
  }

  return false;
}

bool Packet::DecodeUInt32(unsigned int &i)
{
  char *b = rxBuffer->getBuffer();

  if(ascii2uint(&b,2) == eGPSIM_TYPE_UINT32) {

    i = ascii2uint(b,8);
    rxBuffer->index += 2+8;

    return true;
  }
  return false;
}

bool Packet::DecodeUInt64(guint64  &i)
{
  char *b = rxBuffer->getBuffer();

  if(ascii2uint(&b,2) == eGPSIM_TYPE_UINT64) {

    i = ascii2uint64(b,16);
    rxBuffer->index += 2+16;

    return true;
  }
  return false;
}

bool Packet::DecodeBool(bool  &b)
{
  char *buff = rxBuffer->getBuffer();

  if(ascii2uint(&buff,2) == eGPSIM_TYPE_BOOLEAN) {

    if(*buff == '0') 
      b = false;
    else if(*buff == '1')
      b = true;
    else
      return false;

    rxBuffer->index += 2+1;

    return true;
  }
  return false;
}

bool Packet::DecodeFloat(double  &d)
{
  char *b = rxBuffer->getBuffer();

  if(ascii2uint(&b,2) == eGPSIM_TYPE_FLOAT) {

    double dtry = strtod(b, &b);
    unsigned int len = b - rxBuffer->buffer; 
    if( len < rxBuffer->size - rxBuffer->index) {

      rxBuffer->index += len;
      d = dtry;
      return true;
    }
  }
  return false;
}

bool Packet::DecodeObjectType(unsigned int &i)
{
  i = ascii2uint(rxBuffer->getBuffer(),2);
  rxBuffer->index += 2;

  return true;
}

bool Packet::DecodeString(char *retStr, int maxLen)
{

  char *b = rxBuffer->getBuffer();

  if(ascii2uint(&b,2) == eGPSIM_TYPE_STRING) {
    int length = ascii2uint(&b,2);

    maxLen--;   // reserve space for a terminating 0.

    length = (maxLen < length) ? maxLen  : length;

    strncpy(retStr, b, length);
    retStr[length] = 0;

    //*buffer = b + length;

    rxBuffer->index += 2+2+length;
    return true;
  }

  return false;
}

bool Packet::EncodeHeader()
{

  txBuffer->putc('$');
  txBuffer->terminate();

  return true;
}

bool Packet::EncodeUInt32(unsigned int i)
{

  txBuffer->putc(i2a(eGPSIM_TYPE_UINT32 /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_UINT32 ));

  for(int j=7; j>=0; j--)
    txBuffer->putc ( i2a ( i>> (4*j)));

  return true;
}

bool Packet::EncodeUInt64(guint64  i)
{

  txBuffer->putc(i2a(eGPSIM_TYPE_UINT64 /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_UINT64 ));

  for(int j=15; j>=0; j--)
    txBuffer->putc ( i2a ( i>> (4*j)));

  return true;
}


bool Packet::EncodeObjectType(unsigned int i)
{
  EncodeHeader();

  //txBuffer->putc(i2a(eGPSIM_TYPE_OBJECT /16));
  //txBuffer->putc(i2a(eGPSIM_TYPE_OBJECT ));

  txBuffer->putc ( i2a ( i>> (4*1)));
  txBuffer->putc ( i2a ( i>> (4*0)));

  return true;

}

bool Packet::EncodeBool(bool b)
{

  txBuffer->putc(i2a(eGPSIM_TYPE_BOOLEAN /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_BOOLEAN ));

  if(b)
    txBuffer->putc('1');
  else
    txBuffer->putc('0');


  return true;
}

bool Packet::EncodeFloat(double  d)
{
  txBuffer->putc(i2a(eGPSIM_TYPE_FLOAT /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_FLOAT ));

  char buff[256];

  snprintf(buff,sizeof(buff),"%8E~",d);

  txBuffer->puts(buff,strlen(buff));

  return true;
}


bool Packet::EncodeString(const char *str, int len)
{
  if(!str)
    return false;

  txBuffer->putc(i2a(eGPSIM_TYPE_STRING /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_STRING ));

  if(len < 0)
    len = strlen(str);

  txBuffer->putc(i2a(len>>4));
  txBuffer->putc(i2a(len));

  txBuffer->puts(str, len);

  return true;
}

bool Packet::EncodeCustom(const char *str, int len)
{
  if(!str)
    return false;

  txBuffer->putc(i2a(eGPSIM_TYPE_CUSTOM /16));
  txBuffer->putc(i2a(eGPSIM_TYPE_CUSTOM ));

  txBuffer->putc(i2a(len>>4));
  txBuffer->putc(i2a(len));

  txBuffer->puts(str, len);

  return true;
}


syntax highlighted by Code2HTML, v. 0.9.1