#include "serial_communications.h"

/** @name packetise.c 

    This file contains functions pertaining to the construction of packets.

    @author Daniel Storey, UWA, 1998
    @version 1.0   */

/*@{*/

/*************************************************************************/
/** Encode the length of data in a regular packet.
    Encode an integer representing packet data length as an ASCII string.

    @param len int, representing the length which is to be encoded.
    @param a string, in which the encoded value is to be written.
    @return void. */
/*************************************************************************/
void int_2_packet_length(char a[3], int len)
{
  int base = 80, offset = 48;
  char i = 0, j = 0;

  if(len<base) {
    i = len;
    j = 0;
  }
  else {
    i = len % base;
    j = (len - i) / base;
  }
  i += offset;
  j += offset;

  a[0] = j;
  a[1] = i;
  a[2] = '\0';
}

/*************************************************************************/
/** Identify the length of data in a regular packet.
    Convert an ASCII packet string to an integer.

    @param a string, an ASCII encoded string representing packet data length
    @return int, which should be used to determine the length of data in a regular packet. */
/*************************************************************************/
int packet_length_2_int(char a[3])
{
  int base = 80, offset = 48;
  
  return((a[1]-offset) + base * (a[0]-offset)); 
}

/*************************************************************************/
/** Identify whether the packet is special or regular.
    Use the packet type field to identify whether a packet is special or not.

    @param type string, the packet type field of a packet.
    @return bool, identifying if the packet is of the special type. */
/*************************************************************************/
bool is_special_packet(char type[3])
{
  if(type[0]==SPECIAL_PACKET_START_SYMBOL)
    return TRUE;
  else 
    return FALSE;
}

/*************************************************************************/
/** Identify the length of a special packet.
    Use the packet type field to identify the length of a special packet.

    @param type string, the packet type field of a packet.
    @return int, the length of the packet, or -1 if the packet type passed is invalid. */
/*************************************************************************/
int special_packet_length(char type[3])
{
  static char symbol[] = SPECIAL_PACKET_SYMBOLS;
  static int  length[] = SPECIAL_PACKET_LENGTHS;
  int i = 0;

  while (i < NUMBER_OF_PACKET_SYMBOLS) {
    if(type[1]!=symbol[i])
      return length[i];
    else
      i++;
  }
  return -1; /* Error */
}

/*************************************************************************/
/** Determine the length of any packet.
    Use the packet type field to determine the length of the packet.

    @param type string, the packet type field of a packet.
    @return int, the length of the packet, or -1 if the packet type passed is invalid. */
/*************************************************************************/
int packet_length(char type[3])
{
  int length;

  if (is_special_packet(type)==FALSE) {
    length = packet_length_2_int(type);
  }
  else {
    length = special_packet_length(type);
  }
  return length;
}

/*************************************************************************/
/** Generate a packet.
    Use the given information to generate a packet.

    @param packet Packet, the packet to which data is to be written.
    @param address string, the desintation address of the packet.
    @param type string, the type of packet that is to be generated.
    @param data string, the data that is to be written to the packet.
    @param channel Channel, the variable holding the channel data.
    @return Packet, the completed packet. */
/*************************************************************************/
Packet generate_packet(Packet packet, const char* address, const char* type, const char* data, Channel *channel) 
{
  int length = 0;
  
  /* Start frame */
  packet.start[0] = PACKET_START;
  packet.start[1] = '\0';
  
  /* Destination Address */
  strncpy(&packet.destination_address[0], address, ADDRESS_LEN);
  packet.destination_address[ADDRESS_LEN] = '\0';
  
  /* Source Address */
  strncpy(&packet.source_address[0], &channel->my_address[0], ADDRESS_LEN);
  packet.source_address[ADDRESS_LEN] = '\0';
  
  /* Number */
  packet.number[0] = increment_packet_number(channel);
  packet.number[1] = '\0';
  
  /* Frame Type */
  strncpy(&packet.type[0], type, PACKET_TYPE_BYTES);
  packet.type[PACKET_TYPE_BYTES] = '\0';
  
  /* Data */
  length = packet_length(packet.type);
  if((data[0]!='\0')&&(data!=NULL)) {
    strncpy(&packet.data[0], data, length);
    packet.data[length] = '\0';
  }
  else {
    packet.data[0] = '\0';
  }

  /* Define and Add CRC Code */
  crc(&packet.crc[0], packet.data);

  /* End of packet symbol */
  packet.end[0]= PACKET_END;
  packet.end[1]='\0';

  /* Packet has not yet been sent */
  packet.sent = 0;

  return packet;
}

/*************************************************************************/
/** Generate the preamble.
    Write the preamble to the channel variable.

    @param channel Channel, the variable holding the channel data.
    @return void. */
/*************************************************************************/
void write_preamble(Channel *channel)
{
  int  i;
  
  /* preamble */
  for (i=0; i<(PREAMBLE_BYTES-LOCK_BYTES-AFTER_BYTES); i++)
    channel->preamble[i]=0x55;
  /* lock UART */
  for (i=0; i<LOCK_BYTES; i++)
    channel->preamble[PREAMBLE_BYTES-LOCK_BYTES-AFTER_BYTES+i]= (unsigned char) 0xff;
  for(i=0; i<AFTER_BYTES; i++)
    channel->preamble[PREAMBLE_BYTES-AFTER_BYTES+i]= (unsigned char) 0x55;
       
  /* Add end of string Character */
  channel->preamble[PREAMBLE_BYTES]='\0';
  
  return;
}

/*************************************************************************/
/** Break a string into a packet.
    Break the string that is passed to the function and place it in a packet.

    @param packet Packet, the packet to which data is to be written.
    @param address string, the string from which the data is to be taken.
    @return Packet, the completed packet. */
/*************************************************************************/
Packet receive_packet(Packet packet, const char* string)
{  
  int i = 0, j = 0, length = 0;
  
  /* Start */
  packet.start[0] = PACKET_START;
  packet.start[1] = '\0';
  i++;

  /* Destination Address */
  for(j=0; j<ADDRESS_LEN; j++) {
    packet.destination_address[j] = string[i];
    i++;
  }
  packet.destination_address[ADDRESS_LEN]='\0';

  /* Source Address */
  for(j=0; j<ADDRESS_LEN; j++) {
    packet.source_address[j] = string[i];
    i++;
  }  
  packet.source_address[ADDRESS_LEN]='\0';

  /* Packet Number */
  packet.number[0] = string[i];
  packet.number[1] = '\0';
  i++;

  /* Type */
  for(j=0; j<PACKET_TYPE_BYTES; j++) {
    packet.type[j] = string[i];
    i++;
  }
  packet.type[PACKET_TYPE_BYTES] = '\0';

  /* Data */
  length = packet_length(packet.type);
  
  for(j=0; j<length; j++) {
    packet.data[j] = string[i];
    i++;
  }
  packet.data[length]='\0';

  /* CRC */
  if(string[i]==PACKET_END) { /* No CRC in the packet */
    packet.crc[0] = '\0';
  }
  else {
    for(j=0; j<CRC_BYTES; j++) {
      packet.crc[j] = string[i];
      i++;
    }
    packet.crc[CRC_BYTES] = '\0';
  }

  /* End */
  packet.end[0] = PACKET_END;
  packet.end[1] = '\0';

  /* Sent */
  packet.sent = 0;

  return packet;
}

/*************************************************************************/
/** Generate an ok packet.
    Use the given information to generate a special packet that notifies the sending station that the packet that has been passed by this function has been received by this station.

    @param packet Packet, the packet from which information is to be taken.
    @param channel Channel, the variable holding the channel data.
    @return Packet, the completed packet. */
/*************************************************************************/
Packet generate_packet_ok(const Packet packet, Channel *channel)
{
  Packet ok_packet;
  char data[] = { '\0' };
  char type[3] = { SPECIAL_PACKET_START_SYMBOL, 'R', '\0' };
  int number;

  number = packet.number[0];
  
  /* generate a resend packet */
  ok_packet = generate_packet(ok_packet, &packet.source_address[0], &type[0], &data[0], channel); 
  /* Change packet number to be equal to argument given */
  ok_packet.number[0] = number; 
  /* Undo change in number by function 'generate packet */
  decrement_packet_number(channel);
  /* Clear the crc part of the packet */
  ok_packet.crc[0] = '\0';

  return ok_packet;
}
/*************************************************************************/
/** Generate a token packet.
    Use the information stored in the channel variable to generate a token packet to pass control of the channel to the next station.

    @param channel Channel, the variable holding the channel data.
    @return Packet, the completed token packet. */
/*************************************************************************/
Packet generate_token_packet(Channel *channel)
{
  Packet packet;
  char data[] = { '\0' };
  char type[3] = { SPECIAL_PACKET_START_SYMBOL, 'T', '\0' };
  
  /* generate a token packet */
  packet = generate_packet(packet, &channel->next_address[0], &type[0], &data[0], channel);

  /* Remove CRC from packet */
  packet.crc[0] = '\0';

  return packet;
}

/*************************************************************************/
/** Increment the channel to indicate a packet has been sent.
    Carefully change the value of last_packet_number to indicate that a channel has been sent.

    @param channel Channel, the variable holding the channel data.
    @return char, the value of last_packet_number in the channel variable. */
/*************************************************************************/
char increment_packet_number(Channel *channel)
{
  if(channel->last_packet_number == 126)
    channel->last_packet_number = 46;
  else
    channel->last_packet_number++;

  return channel->last_packet_number;
}

/*************************************************************************/
/** Increment the channel to indicate a packet has been sent.
    Carefully decrement  the value of last_packet_number.  This is used when a special packet is generated to ensure packet numbers are not wasted.

    @param channel Channel, the variable holding the channel data.
    @return char, the value of last_packet_number in the channel variable. */
/*************************************************************************/
void decrement_packet_number(Channel *channel)
{
  if(channel->last_packet_number == 46)
    channel->last_packet_number = 126;
  else
    channel->last_packet_number--;
}








