#include "serial_communications.h"

/** @name low-level.c 

    This file contains functions pertaining to the bottom layer of the communications structure.

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

/*@{*/

/*************************************************************************/
/** Checks if we can send.
    Determines the status of the token flag and returns true if we can send.

    @param channel Channel, the variable holding the channel data.
    @return bool, true if we can send. */
/*************************************************************************/
bool can_send(Channel *channel)
{
  return channel->token;
}

/*************************************************************************/
/** Send all packets in the outgoing queues.
    Get packets from the outgoing queues, then send them , along with the token packet to transfer control to the next station in the network.  Increment the value of the "sent" field of every regular packet.  Remove those packets which are too old.

    @param channel Channel, the variable holding the channel data.
    @return void. */
/*************************************************************************/
void low_send(Channel *channel)
{
  Packet packet;
  int i = 0, j = 0;
  char c = (char) 0x55;
  
  /* check token status */
  if(channel->token==FALSE) {
    #if DEBUG
      LCDPrintf("Error: low send called before token received\n");
    #endif
    return; /* Do Not Send */
  }
  
  /* send preamble */
  send_string(channel->preamble, PREAMBLE_BYTES, channel);
  /* Check queue for packets which are too old */
  for(j=0; j<QUEUE_ELEMENTS; j++) {
    if(valid_queue_entry(&channel->sending, j)) {
      increment_packet_in_queue(&channel->sending, j);
      packet = get_from_queue(&channel->sending, j);
      if(packet.sent >= MAXIMUM_NUMBER_OF_RESENDS ) {
	remove_from_queue(&channel->sending, j);
	/* LCDPrintf("Err: '%s'\n", packet.data); */
/* 	KEYWait(KEY1); */
      }
      else {  /* Send Packets */
	send_packet(packet, FALSE, channel);
      }
    }
  }
  
  /* Send Special Packets */
  while(special_queue_length(&channel->special_sending)!=0) {
    packet = special_dequeue(&channel->special_sending);
    send_packet(packet, TRUE, channel);
  }
  
  /* Pass the token */
  packet = generate_token_packet(channel);
  send_packet(packet, TRUE, channel);
  
  /* Change token status */
  channel->token = FALSE;

  /* Add some dummy characters */
  for(i=0; i<DUMMY_CHARS; i++)
    OSSendBuff(&c);
  
#if TOKEN_MSG
  LCDPrintf("TOKEN SENT\n"); 
#endif
  
  return;
}

/*************************************************************************/
/** Send each string that makes up a packet.
    Take each component of a packet and pass it to the "send_string" routine.
    
    @param channel Channel, the variable holding the channel data.
    @param packet Packet, the packet that is to be sent.
    @param special bool, true if the packet is special (No CRC).
    @return void. */
/*************************************************************************/
void send_packet(Packet packet, bool special, Channel *channel)
{
  send_string(packet.start, 1, channel);
  send_string(packet.destination_address, ADDRESS_LEN, channel);
  send_string(packet.source_address, ADDRESS_LEN, channel);
  send_string(packet.number, 1, channel);
  send_string(packet.type, PACKET_TYPE_BYTES, channel);
  send_string(packet.data, packet_length(packet.type), channel);
  if(special==FALSE)
    send_string(packet.crc, CRC_BYTES, channel);
  send_string(packet.end, 1, channel);

  
  return;
}

/*************************************************************************/
/** Determine whether this machine owns the potential packet.
    Read through a raw string to determine whether the address matches that of this station.
    
    @param channel Channel, the variable holding the channel data.
    @param string string, a raw string with a start character at the beginning and an end character at the end.
    @return bool, true if the address of the string matches the address of this machine. */
/*************************************************************************/
bool packet_owner(char* string, Channel *channel)
{
  int i = 1, j, temp;
  char destination_address[ADDRESS_LEN+1];
  
  /* Obtain Address from string */
  for(j=0; j<ADDRESS_LEN; j++) {
    destination_address[j] = string[i];
    i++;
  }
  destination_address[ADDRESS_LEN]='\0';

  /* Compare broadcast address and packet address */
  temp = strcmp(&destination_address[0], &channel->broadcast_address[0]);
 
  if(temp==0) {
    return TRUE; /* Destination is this machine */
  }
  
  /* Compare machine address and packet address */
  temp = strcmp(&destination_address[0], &channel->my_address[0]);
 
  if(temp==0) {
    return TRUE; /* Destination is this machine */
  }  
  else { 
    return FALSE; /* Destination is not this machine */
  }
}

/*************************************************************************/
/** Determine if the CRC in a packet that has been received is correct.
    Get the CRC from a packet that has been received, generate the correct CRC and compare this with the CRC that has been received.
    
    @param packet Packet, the packet for which the crc is to be compared.
    @return bool, true if the CRC is correct. */
/*************************************************************************/
bool check_crc(Packet *packet)
{
  char calc_crc[9];
  int i;
  
  crc(&calc_crc[0], &packet->data[0]);

  /* Compare Strings */
  for(i=0; i<7; i++) {
    if(calc_crc[i]!=packet->crc[i]) {
      return FALSE;
    }
  }
  return TRUE;
}

/*************************************************************************/
/** Determine if a raw packet string is long enough to allow conversion.
    Get the length field from a raw packet string, then determine whether their is sufficient string left to justify conversion of this string into a packet.
    
    @param string string, the raw packet string.
    @return bool, true if there is sufficient packet length. */
/*************************************************************************/
bool enough_string_left(char* string) {
  char* pack_len;
  int string_left;
  
  string_left = string_length(string);
  /* First check there are enough bytes to allow for minimum length */
  if (string_left < (1 + ADDRESS_LEN + ADDRESS_LEN + 1 + 2 + 1)) {
    return FALSE;
  }

  /* set pointer to where packet length should be */
  pack_len = &string[ 1 + ADDRESS_LEN + ADDRESS_LEN + 1];

  
  if (string_left < total_packet_length(pack_len)) {
    return FALSE;
  }
  else {
    return TRUE;
  }
}

/*************************************************************************/
/** Determine if the end chracter of a raw string is correct.
    Get the length field from a raw packet string, then read the character at the end of the packet as determined by this length.
    
    @param string string, the raw packet string.
    @return bool, true if the end byte is correct. */
/*************************************************************************/
bool end_packet_correct(char* string)
{
  char* pack_len;
  
  /* set pointer to where packet length should be */
  pack_len = &string[1+ADDRESS_LEN+ADDRESS_LEN+1];
  

  /* Length of string until end byte */
  if (string[total_packet_length(pack_len)-1] == PACKET_END) /* Length of string until end byte */
    return TRUE;
  else
    return FALSE;
}

/*************************************************************************/
/** Find the total length of a packet.
    From the packet length string that is passed, determine the length of a total packet containing this string.
    
    @param packet_len string, an ASCII encoded string representing packet length.
    @return int, the total length of the packet. */
/*************************************************************************/
int total_packet_length(char* pack_len)
{
  int length = 0;
  
  if(is_special_packet(pack_len)) {
    length = 1 + ADDRESS_LEN + ADDRESS_LEN + 1 + PACKET_TYPE_BYTES + packet_length(pack_len) + 1;
  }
  else {
    length = 1 + ADDRESS_LEN + ADDRESS_LEN + 1 + PACKET_TYPE_BYTES + packet_length(pack_len) + CRC_BYTES + 1;
  }
  
  return length;
}

/*************************************************************************/
/** Check if a token has been received.
    Access the channel variable to see if this station holds the token.
    
    @param channel Channel, the variable holding the channel data.
    @return bool, true if the token is held by this station. */
/*************************************************************************/
bool free_to_send(Channel *channel)
{
  return channel->token;
}

/*************************************************************************/
/** Extract a packet from a raw string.
    Perform checks on a raw packet string.  If the string passes these tests, place the decoded data string in the receiving queue, and an ok packet in the special packet sending queue.
    
    @param channel Channel, the variable holding the channel data.
    @param string string, the raw packet string.
    @return void. */
/*************************************************************************/
void extract_packet(char* string, Channel *channel)
{ 
  Packet packet, ok;
  bool a;
#if PC_SERIAL_CHECK
  char message[10];
#endif
  
#if PC_SERIAL_CHECK
  send_check_str("Extract\n", 8);
#endif
  /* Check if there is sufficient packet left according to packet.length */
  if(enough_string_left(&string[0])) {
#if PC_SERIAL_CHECK
    send_check_str("Enough String\n", 14);
#endif
    /* Check if this machine is the packet owner */
    if(packet_owner(&string[0], channel)) {
#if PC_SERIAL_CHECK      
      send_check_str("Own\n", 4);
#endif
      /* Check if the end of packet marker is correct */
      if(end_packet_correct(&string[0])) {
#if PC_SERIAL_CHECK 
	send_check_str("End\n", 4);
#endif
	/* Break the string into a packet */
	packet = receive_packet(packet, &string[0]);
	/* Check if the packet is a special packet */
	if(is_special_packet(packet.type)) {
#if PC_SERIAL_CHECK
	  send_check_str("Special\n", 8);
#endif
	  /* do some stuff if its a special packet */
	  switch(packet.type[1])
	  {
	  case('R'):
	    /* Packet has been received ok */
	    a = match_and_remove(&channel->sending, packet.number[0]);
#if PC_SERIAL_CHECK
	    if(a)
	      send_check_str("Removed\n", 8);
	    else send_check_str("Rem: Couldn't find\n", 19);
#endif
	    return;
	  case('T'):
	    /* Token - change channel v'ble 'token' so we can send */
	    channel->token=TRUE;
#if TOKEN_MSG
	    LCDPrintf("TOKEN RECEIVED\n");
#endif
#if PC_SERIAL_CHECK
	    send_check_str("Token Wrong Place\n", 18);
#endif
	    /* Increment the age of all received frames in the received frames list */
	    recframe_increment_age(&channel->recframe_queue);
	    return;
	  } 
	}
	else {
	  /* Normal packet */
	  /* Check the CRC of the incoming packet */
#if PC_SERIAL_CHECK
	  send_check_str("Normal\n", 7);
#endif
	  if(check_crc(&packet)) {
	    /* Frame structure is sound.  Check recent packets for duplicate */
#if PC_SERIAL_CHECK
	    send_check_str("CRC\n", 4);
#endif
	    if(!recframe_match(&channel->recframe_queue,packet.number,packet.source_address)) {
#if PC_SERIAL_CHECK
	      send_check_str("Not a Duplicate\n", 16);
#endif
	      /* Add this frame to recent arrivals */
	      recframe_enqueue(&channel->recframe_queue,packet.number,packet.source_address);
	      string_enqueue(&channel->string_receiving, packet.data, packet.source_address);
	    }
	    /* Generate & queue response */
	    ok = generate_packet_ok(packet, channel);
#if PC_SERIAL_CHECK
	    send_check_str("Response\n", 9);
#endif
	    special_enqueue(&channel->special_sending, ok);     
	  }
	}
      }
    }
  }

  return;
}

/*************************************************************************/
/** Check a raw frame for a token.
    Quickly check for the most common frame type to improve efficiency.

    @param string string, the raw frame.
    @param channel Channel, the variable holding the channel data.
    @return bool, True if the raw frame is a token. */
/*************************************************************************/
bool fast_token_check(char* string, Channel *channel)
{
  if(string_length(string)==7)
    if(strncmp(&string[1], channel->my_address, ADDRESS_LEN)==0)
      if(string[5]=='T') {
	  /* Token - change channel v'ble 'token' so we can send */
	    channel->token=TRUE;
#if TOKEN_MSG
	    LCDPrintf("TOKEN RECEIVED\n");
	    send_check_str("Token\n", 6);
#endif
	    /* Increment the age of all received frames in the received frames list */
	    recframe_increment_age(&channel->recframe_queue);
	    return TRUE;
      }
  return FALSE;
}

/*************************************************************************/
/** Receive data at the lowest level.
    Read raw packets from the seria port, then decode and queue these until a token that passes control to send to this station is received.
    
    @param channel Channel, the variable holding the channel data.
    @return int, always 0. */
/*************************************************************************/
int low_receive(Channel *channel)
{
  /* Check if a token is present */
  if(can_send(channel))
    return 0;
  
  /* Read raw packet from port */
  if(read_until_packet_end(channel)!=-1) {
    if(!fast_token_check(channel->raw, channel))
      extract_packet(channel->raw, channel);
#if DEBUG
    LCDPrintf("Extracting\n");
#endif
    channel->timeout=0;
  }
  else channel->timeout++;
  
  /* Check if a timeout is called */
  if(channel->timeout>MAX_TIMEOUT_DELAY) {
    if(channel->master==TRUE) {
      force_send(channel); /* Generate another token at master */
    }
#if TOKEN_MSG
    LCDPrintf("Timeout Error\n");
    LCDPrintf("Partial:'%s'\n", channel->raw);
    KEYWait(KEY1);
#endif
    channel->timeout = 0;
  }
  return 0;
}













