#include "serial_communications.h"

/** @name top-level.c 

    This file contains functions in the highest layer.

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

/*@{*/

/*************************************************************************/
/** Initiate the communications system.
    Set all channel variables, initiate the serial port.

    @param my_address string, which contains the address of this station.
    @param next_address string, which contains the address of the next station in the control loop.
    @return Channel, the variable containing the characteristics of the link. */
/*************************************************************************/

Channel initiate_serial_communications(char* my_address, char* next_address, char* broadcast_address, int baudrate, bool wireless, bool master)
{ 
  Channel channel;

  /* set baud rate */
  channel.baudrate = baudrate;
  
  /* Initialise packet vairables */
  channel.last_packet_number = 36;
  
  /* Initialise Addresses */
  strncpy(&channel.my_address[0], my_address, ADDRESS_LEN);
  channel.my_address[ADDRESS_LEN] = '\0';
  strncpy(&channel.next_address[0], next_address, ADDRESS_LEN);
  channel.next_address[ADDRESS_LEN] = '\0';
  strncpy(&channel.broadcast_address[0], broadcast_address, ADDRESS_LEN);
  channel.broadcast_address[ADDRESS_LEN] = '\0';
  
  /* write the preamble */
  write_preamble(&channel);
    
  /* Initialise the recent received frame data queue */
  initialise_recframe_queue(&channel.recframe_queue);
  
  /* Initialise Special Sending Queue */
  initialise_special_queue(&channel.special_sending);
  
  /* Initialise Receive Queue */
  initialise_string_queue(&channel.string_receiving);
  
  /* Initialise Send Queue */
  initialise_queue(&channel.sending);

  /* initialise error queue */
  initialise_queue(&channel.error);

  /* Initialise token status */
  channel.token = FALSE;

  /* Initialise physical link */
  initalise_serial_port(channel);

  /* Set the wireless */
  channel.wireless = wireless;
  
  /* Set the transceiver to receive mode */
  tx_receive();

  /* Set the raw frame status */
  channel.raw_valid=FALSE;
  channel.raw_length=0;
    
  /* Set the number of timeouts to 0 */
  channel.timeout=0;
  
  /* Allow a send if master */
  if(master==TRUE) {
    channel.master = TRUE;
    force_send(&channel);
  }
  else
    channel.master = FALSE;
  
  return channel;
}

/*************************************************************************/
/** Allow the station to send without a token.
    Set the relevant part of the communications channel.
    
    @param channel Channel, the communication link variable.
    @return void. */
/*************************************************************************/
void force_send(Channel *channel)
{
  channel->token = TRUE;
}

/*************************************************************************/
/** Send a string.
    Send a string to the station indicated over the channel indicated.

    @param array char*, the string/array to be sent. 
    @param address string, the address of the station to send to.
    @param length int, the length of the string/array to be sent.
    @param channel Channel, the communication link variable.
    @return int, 0 if sucessful, -1 if unsucessful. */
/*************************************************************************/
int comms_send(const char* array, char* address, int length, Channel *channel)
{
  char packet_type[3], string[MAXIMUM_DATA_LENGTH];
  Packet packet;

  /* Check the queue length */
  if(queue_length(&channel->sending)>=QUEUE_ELEMENTS)
    return FALSE;

  /* Check packet length, return error information if packet too long */
  if (length > MAXIMUM_DATA_LENGTH) {
    LCDPrintf("Comms Error:\nLongPacket Length\n");
    return FALSE;
  }

  /* Encode the packet */
  convert_to_comms_string(string, array, length);
  
  /* Find the length of the encoded string */
  length = string_length(string);
  
  /* Generate Packet type information */
  int_2_packet_length(packet_type, length);
  
  /* Generate Packet */
  packet = generate_packet(packet, address, packet_type, string, channel);
  
  /* Add to send Queue */
  enqueue(&channel->sending, packet);

  /* Send o.k. */
  return TRUE;
}

/*************************************************************************/
/** Send a string.
    Send a string to the station indicated over the channel indicated.

    
    @param channel Channel, the communication link variable.
    @param string string, the string that has been received.
    @return int, 0 if sucessful, -1 if unsucessful. */
/*************************************************************************/
int comms_receive(char *array, char *address, Channel *channel)
{
  char string[MAXIMUM_DATA_LENGTH];
  
  /* Read next packet from receive queue */
  if(string_queue_length(&channel->string_receiving)==0) {
    string[0] = '\0';
    return -1;
  }
  
  /* Dequeue next string */
  string_dequeue(&channel->string_receiving, string, address);
  
  /* Decode the string */
  decipher_comms_string(array, string);
  
  /* Return ok */
  return 0;
}

/*************************************************************************/
/** Return the number of packets in the receive queue.
    Access the appropriate queue and return the number of strings in the q.

    
    @param channel Channel, the communication link variable.
    @return int, the number of packets in the queue. */
/*************************************************************************/
int receive_queue(Channel *channel)
{
  return string_queue_length(&channel->string_receiving);
}

/*************************************************************************/
/** Return the number of packets in the error queue.
    Return the number of strings have been sent but for which there has been no reply confirming recipt.

    @param string string, the string variable to be written to.
    @param channel Channel, the communication link variable.
    @return int, the number of errors in the queue. */
/*************************************************************************/
char* comms_error(char* string, Channel *channel)
{
  int length;
  Packet packet;

  /* Read next packet from error queue */
  if(!empty(&channel->error))
     packet = dequeue(&channel->error);
  else {
    string[0] = '\0';
  };
  
  /* Copy data to string */
  length = packet_length(packet.type);
  strcpy(&string[0], &packet.start[0]);

  /* Return pointer to the string */
  return &string[0];
}

/*************************************************************************/
/** Return the number of packets in the output error queue.
    Return the number of packets in the error queue (o/p of comms).

    @param channel Channel, the communication link variable.
    @return int, the number of errors in the output queue. */
/*************************************************************************/
int error_queue(Channel *channel)
{
  return queue_length(&channel->error);
}

/*************************************************************************/
/** Map special characters.
    Take a character in an array and process it to return the mapped character.

    @param character char, the character that is to be encoded.
    @return char, the character that has been encoded. */
/*************************************************************************/
char map_char(const char character)
{
  switch (character) {
  case '\0':
    return 'A';
    break;
  case COMMS_STRING_SPECIAL_CHARACTER_INDICATOR:
    return 'B';
    break;
  case PACKET_START:
    return 'C';
    break;
  case PACKET_END:
    return 'D';
    break;
  default:
    return character;
  }
}

/*************************************************************************/
/** Un-map special characters.
    Take a character in a string and process it to return the un-mapped character.

    @param character char, the character that is to be decoded.
    @return char, the character that has been decoded. */
/*************************************************************************/
char unmap_char(const char character)
{
  switch (character) {
  case 'A': 
    return '\0'; 
    break; 
  case 'B':
    return COMMS_STRING_SPECIAL_CHARACTER_INDICATOR;
  case 'C':
    return PACKET_START;
    break;
  case 'D':
    return PACKET_END;
    break;
  default:
    return character;
  }
}

/*************************************************************************/
/** Convert an array to a communications string.
    Take an array, and map the characters so it can be transmitted.

    @param string string, the output string that has been encoded.
    @param array char*, the array that is to be encoded.
    @param length int, the length of the array to be encoded.
    @return void. */
/*************************************************************************/
void convert_to_comms_string(char* string, const char* array, int length)
{
  int i = 0, j = 0, flag = 0;
  char c;

  for(i=0; i<length; i++) {
    /* map the 0's to 60's */
    flag = 0;
    if(array[i]==0) {
      string[j]=60;
      j++;
      flag = 1;
    }
    
    /* map the 60's to 0's, then encode them */
    if(array[i]==60) {
      string[j]= COMMS_STRING_SPECIAL_CHARACTER_INDICATOR;
      j++;
      string[j]= map_char('\0');
      j++;
      flag = 1;
    }
    
    /* map other characters */
    if(flag==0) {
      c = map_char(array[i]);
      if(c!=array[i]) {
	string[j]= COMMS_STRING_SPECIAL_CHARACTER_INDICATOR;
	j++;
	string[j]= c;
	j++;
      }
      else {
	string[j] = array[i];
	j++;
      }  
    }
  }
  string[j] = '\0';
  
  return;
}

/*************************************************************************/
/** Convert a communications string to an array.
    Take a string that has been received, and map the characters to form the original array.

    @param string string, the received string.
    @param array char*, the array that has been decoded.
    @return void. */
/*************************************************************************/
void decipher_comms_string(char* array, const char* string)
{
  int length = 0, i = 0, j = 0, flag;

  length = strlen(string);

  for(i=0; i<length; i++) {

    /* map all the characters back */
    if(string[i]==COMMS_STRING_SPECIAL_CHARACTER_INDICATOR) {
      i++;
      array[j]= unmap_char(string[i]);
      j++;
      
    }
    else {
      array[j] = string[i];
      j++;
    } 

    /* cross-map the 60's and the 0's */
    j--;
    flag = 0;
    if(array[j]==60) {
      array[j]=0;
      flag = 1;
    }
    if((flag==0)&&array[j]==0)
      array[j] = 60;
    j++;
    
  }

  array[j] = '\0';
  
  return;
}










