/*
 * Copyright (c) 2008, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is part of the Contiki operating system.
 *
 * $Id: sicslowmac.c,v 1.5 2008/11/08 03:29:15 c_oflynn Exp $
 */


/**
 * \file
 *         Example glue code between the existing MAC code and the
 *         Contiki mac interface
 *
 * \author
 *         Adam Dunkels <adam@sics.se>
 *         Eric Gnoske <egnoske@gmail.com>
 *         Blake Leverett <bleverett@gmail.com>
 *
 * \addtogroup rf230mac
 */

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include "net/rime/rimebuf.h"
#include "zmac.h"
#include "mac.h"
#include "frame.h"
#include "radio.h"
#include "tcpip.h"
#include "sicslowmac.h"
#include "sicslowpan.h"
#include "ieee-15-4-manager.h"

/* Macros */
#define DEBUG 0
#define MAX_EVENTS 10

#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#define SICSLOW_CORRECTION_DELAY 70
#else
#define PRINTF(...)
#define SICSLOW_CORRECTION_DELAY 7
#endif

#ifdef JACKDAW
#include "sicslow_ethernet.h"
#define LOG_FRAME(x,y) mac_logTXtoEthernet(x,y)
#else
#define LOG_FRAME(x,y)
#endif

/* Globals */
static struct mac_driver mac_driver_struct;
static struct mac_driver *pmac_driver = &mac_driver_struct;
extern ieee_15_4_manager_t ieee15_4ManagerAddress;
static parsed_frame_t *parsed_frame;


const struct mac_driver sicslowmac_driver = {
  sicslowmac_dataRequest,
  /*   read_packet, */
  /*   set_receive_function, */
  /*   on, */
  /*   off, */
};

static struct {
  uint8_t head;
  uint8_t tail;
  event_object_t event_object[MAX_EVENTS];
} event_queue;

/* Prototypes */
static void setinput(void (*r)(const struct mac_driver *d));
void (*pinput)(const struct mac_driver *r);
void sicslowmac_unknownIndication(void);


void (*sicslowmac_snifferhook)(const struct mac_driver *r) = NULL;


/*---------------------------------------------------------------------------*/
/**
 * \brief Checks for any pending events in the queue.
 *
 * \return True if there is a pending event, else false.
 */
uint8_t
mac_event_pending(void)
{
  return (event_queue.head != event_queue.tail);
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Puts an event into the queue of events.
 *
 * \param object is a pointer to the event to add to queue.
 */
void
mac_put_event(event_object_t *object)
{
  uint8_t newhead;

  if ((event_queue.head + 1) % MAX_EVENTS == event_queue.tail){
    /* queue full, get outta here */
    return;
  }

  newhead = event_queue.head;

  /* store in queue */
  event_queue.event_object[newhead] = *object;

  /* calculate new head index */
  newhead++;
  if (newhead >= MAX_EVENTS){
    newhead = 0;
  }
  event_queue.head = newhead;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Pulls an event from the event queue.
 * Assumes that there is an event in the queue.  See mac_event_pending().
 *
 * \return Pointer to the event object, or NULL in the event of empty queue.
 */
event_object_t
*mac_get_event(void)
{
  event_object_t *object = NULL;
  volatile uint8_t newtail;

  newtail = event_queue.tail;

  object = &(event_queue.event_object[newtail]);

  /* calculate new tail */
  newtail++;
  if (newtail >= MAX_EVENTS){
    newtail = 0;
  }

  event_queue.tail = newtail;

  return(object);
}

void mac_pollhandler(void)
{
    mac_task(0, NULL);
}

/*---------------------------------------------------------------------------*/
/**
 * \brief This is the main loop task for the MAC.  Called by the
 * main application loop.
 */
void
mac_task(process_event_t ev, process_data_t data)
{
  /* check for event in queue */
  event_object_t *event;

  if(mac_event_pending()){

      event = mac_get_event();

      /* Handle events from radio */
      if (event){

          if (event->event == MAC_EVENT_RX){
              /* got a frame, find out with kind of frame */
              parsed_frame = (parsed_frame_t *)event->data;
              if (parsed_frame->fcf->frameType == DATAFRAME){
                  sicslowmac_dataIndication();
              } else {
			  
		  		  /* Hook to cath unknown frames */
				sicslowmac_unknownIndication();
			  }
	  

			/* Frame no longer in use */
			parsed_frame->in_use = false;
          }

          if (event->event == MAC_EVENT_DROPPED){
              /* Frame was dropped */
              PRINTF("sicslowmac: Frame Dropped!\n");
          }
      }
  }
}
/*---------------------------------------------------------------------------*/
void
setinput(void (*r)(const struct mac_driver *d))
{
  pinput = r;
}
/*---------------------------------------------------------------------------*/
static uint8_t dest_reversed[8];
static uint8_t src_reversed[8];

void
sicslowmac_dataIndication(void)
{
  rimebuf_clear();

  /* Finally, get the stuff into the rime buffer.... */
  rimebuf_copyfrom(parsed_frame->payload, parsed_frame->payload_length);
  rimebuf_set_datalen(parsed_frame->payload_length);
  
  memcpy(dest_reversed, (uint8_t *)parsed_frame->dest_addr, 8);
  memcpy(src_reversed, (uint8_t *)parsed_frame->src_addr, 8);
  
  /* Change addresses to expected byte order */
  byte_reverse((uint8_t *)dest_reversed, 8);
  byte_reverse((uint8_t *)src_reversed, 8);
  
  rimebuf_set_addr(RIMEBUF_ADDR_RECEIVER, (const rimeaddr_t *)dest_reversed);
  rimebuf_set_addr(RIMEBUF_ADDR_SENDER, (const rimeaddr_t *)src_reversed);

  PRINTF("sicslowmac: hand off frame to sicslowpan \n");
  pinput(pmac_driver);
}

void
sicslowmac_unknownIndication(void)
{
  if (sicslowmac_snifferhook) {

	  rimebuf_clear();

	  /* Finally, get the stuff into the rime buffer.... */
	  rimebuf_copyfrom(parsed_frame->payload, parsed_frame->payload_length);
	  rimebuf_set_datalen(parsed_frame->payload_length);
	  
	  memcpy(dest_reversed, (uint8_t *)parsed_frame->dest_addr, 8);
	  memcpy(src_reversed, (uint8_t *)parsed_frame->src_addr, 8);
	  
	  /* Change addresses to expected byte order */
	  byte_reverse((uint8_t *)dest_reversed, 8);
	  byte_reverse((uint8_t *)src_reversed, 8);
	  
	  rimebuf_set_addr(RIMEBUF_ADDR_RECEIVER, (const rimeaddr_t *)dest_reversed);
	  rimebuf_set_addr(RIMEBUF_ADDR_SENDER, (const rimeaddr_t *)src_reversed);

	  PRINTF("sicslowmac: hand off frame to sniffer \n");
	  
	  sicslowmac_snifferhook(pmac_driver);
	 }
  
}

/*---------------------------------------------------------------------------*/
/**
 * \brief      This is the implementation of the 15.4 MAC Data Request
 *             primitive.
 *
 * \return     Integer denoting success or failure.
 * \retval 0   Failure.
 * \retval 1   Success.
 *
 *             The data request primitive creates the frame header based
 *             on static and dynamic data. The static data will be refined
 *             in phase II of the project. The frame payload and length are
 *             retrieved from the rime buffer and rime length respectively.
 *
 *             When the header and payload are assembled into the
 *             frame_create_params structure, the frame is created
 *             by a call to frame_tx_create and then transmited via
 *             radio_send_data.
 */
/*---------------------------------------------------------------------------*/
int
sicslowmac_dataRequest(void)
{

  _delay_ms(SICSLOW_CORRECTION_DELAY);

   /* create structure to store result. */
  frame_create_params_t params;
  frame_result_t result;

  /* Save the msduHandle in a global variable. */
  msduHandle = rimebuf_attr(RIMEBUF_ATTR_PACKET_ID);

  /* Build the FCF. */
  params.fcf.frameType = DATAFRAME;
  params.fcf.securityEnabled = false;
  params.fcf.framePending = false;
  params.fcf.ackRequired = rimebuf_attr(RIMEBUF_ATTR_RELIABLE);
  params.fcf.panIdCompression = false;

  /* Insert IEEE 802.15.4 (2003) version bit. */
  params.fcf.frameVersion = IEEE802154_2003;

  /* Increment and set the data sequence number. */
  params.seq = macDSN++;

  /* Complete the addressing fields. */
  /**
     \todo For phase 1 the addresses are all long. We'll need a mechanism
     in the rime attributes to tell the mac to use long or short for phase 2.
  */
  params.fcf.srcAddrMode = LONGADDRMODE;
  params.dest_pid = ieee15_4ManagerAddress.get_dst_panid();

  /*
   *  If the output address is NULL in the Rime buf, then it is broadcast
   *  on the 802.15.4 network.
   */
  if(rimeaddr_cmp(rimebuf_addr(RIMEBUF_ADDR_RECEIVER), &rimeaddr_null) ) {
    /* Broadcast requires short address mode. */
    params.fcf.destAddrMode = SHORTADDRMODE;
    params.dest_pid = BROADCASTPANDID;
    params.dest_addr.addr16 = BROADCASTADDR;

  } else {

    /* Phase 1.5 - end nodes send to anyone? */
    memcpy(&params.dest_addr, (uint8_t *)rimebuf_addr(RIMEBUF_ADDR_RECEIVER), LONG_ADDR_LEN);
	
    /* Change from sicslowpan byte arrangement to sicslowmac */
    byte_reverse((uint8_t*)&params.dest_addr.addr64, LONG_ADDR_LEN);

    /* Phase 1 - end nodes only sends to pan coordinator node. */
    /* params.dest_addr.addr64 = ieee15_4ManagerAddress.get_coord_long_addr(); */
    params.fcf.destAddrMode = LONGADDRMODE;
  }

  /* Set the source PAN ID to the global variable. */
  params.src_pid = ieee15_4ManagerAddress.get_src_panid();

  /*
   * Set up the source address using only the long address mode for
   * phase 1.
   */
  params.src_addr.addr64 = ieee15_4ManagerAddress.get_long_addr();

  /* Copy the payload data. */
  params.payload_len = rimebuf_datalen();
  params.payload =  rimebuf_dataptr();

  /* Create transmission frame. */
  frame_tx_create(&params, &result);
  
  /* Log if needed */
  LOG_FRAME(&params, &result);

  /* Retry up to this many times to send the packet if radio is busy */
  uint8_t retry_count = 3;

  while(retry_count) {

	  PRINTF("sicslowmac: sending packet of length %d to radio, result:", result.length);
	  
	    

	  /* Send data to radio. */
	  radio_status_t rv = radio_send_data(result.length, result.frame);

	  if (rv == RADIO_SUCCESS) {
	      PRINTF(" Success\n");

		  return 1; /* True says that the packet could be sent */
      }


	  if (rv != RADIO_WRONG_STATE) {
	      PRINTF(" Failed\n");
	  	  return 0;
      }

	  PRINTF(" Radio busy, retrying\n");

      /** \todo: Fix delay in sicslowmac so they do not block receiving */

       //We have blocking delay here, it is safest this way. BUT doesn't solve the
	   //problem of TX when you are RXing.. as the RX code can't execute!
	  if (retry_count == 3) {
	 	  _delay_ms(10);
	  } else if (retry_count == 2) {
	      _delay_ms(50);
	  } else if (retry_count == 1) {
	      _delay_ms(200);
	  }

	  retry_count--;
  }

  PRINTF("sicslowmac: Unable to send packet, dropped\n");
  return 0;

}
/*---------------------------------------------------------------------------*/
/**
 * \brief Stub function that will be implemented in phase 2 to cause
 * end nodes to sleep.
 */
int
mac_wake(void)
{
  return 1;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Stub function that will be implemented in phase 2 to cause
 * end nodes to sleep.
 */
int
mac_sleep(void)
{
  return 1;
}
/*---------------------------------------------------------------------------*/
const struct mac_driver *
sicslowmac_init(const struct radio_driver *d)
{
  /* AD: commented out the radio_driver code for now.*/
  /*  radio = d;
      radio->set_receive_function(input_packet);
      radio->on();*/

  return &sicslowmac_driver;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief      This is the implementation of the 15.4 MAC Reset Request
 *             primitive.
 * \param      setDefaultPIB True if the default PIB values should be set.
 * \return     Integer denoting success or failure.
 * \retval 0   Failure.
 * \retval 1   Success.
 *
 *             Sets all PIB values to default.
 */
void
sicslowmac_resetRequest (bool setDefaultPIB)
{
  if(setDefaultPIB){
    /* initialize all of the MAC PIB variables to their default values */
    macCoordShortAddress = 0xffff;
    macDSN = rand() % 256;
    macSrcPANId = SOURCE_PAN_ID;
    macDstPANId = DEST_PAN_ID;
    macShortAddress = 0xffff;
    /* Setup the address of this device by reading a stored address from eeprom. */
    /** \todo   This might be read from the serial eeprom onboard Raven. */
    AVR_ENTER_CRITICAL_REGION();
    eeprom_read_block ((void *)&macLongAddr, EEPROMMACADDRESS, 8);
	
	byte_reverse((uint8_t *) &macLongAddr, 8);
	
	
    AVR_LEAVE_CRITICAL_REGION();
  }
}

parsed_frame_t * sicslowmac_get_frame(void)
{
    return parsed_frame;
}

/*---------------------------------------------------------------------------*/
struct mac_driver * sicslowmac_get_driver(void)
{
	return pmac_driver;
}
/*---------------------------------------------------------------------------*/
PROCESS(mac_process, "802.15.4 MAC process");
PROCESS_THREAD(mac_process, ev, data)
{

  PROCESS_POLLHANDLER(mac_pollhandler());


  PROCESS_BEGIN();

  radio_status_t return_value;

  /* init radio */
  /** \todo: this screws up if calosc is set to TRUE, find out why? */
  return_value = radio_init(false, NULL, NULL, NULL);

#if DEBUG
  if (return_value == RADIO_SUCCESS) {
    printf("Radio init successful.\n");
  } else {
    printf("Radio init failed with return: %d\n", return_value);
  }
#endif

  uint8_t eeprom_channel;
  uint8_t eeprom_check;
  
  eeprom_channel = eeprom_read_byte(9);
  eeprom_check = eeprom_read_byte(10);
  
  if ((eeprom_channel < 11) || (eeprom_channel > 26) || ((uint8_t)eeprom_channel != (uint8_t)~eeprom_check)) {
	eeprom_channel = 24; //Default
  }

  radio_set_operating_channel(eeprom_channel);
  radio_use_auto_tx_crc(true);
  radio_set_trx_state(TRX_OFF);

  mac_init();

  /* Set up MAC function pointers and sicslowpan callback. */
  pmac_driver->set_receive_function = setinput;
  pmac_driver->send = sicslowmac_dataRequest;
  sicslowpan_init(pmac_driver);

  ieee_15_4_init(&ieee15_4ManagerAddress);

  radio_set_trx_state(RX_AACK_ON);

  while(1) {
    PROCESS_YIELD();
    mac_task(ev, data);

  }

  PROCESS_END();
}

void byte_reverse(uint8_t * bytes, uint8_t num)
{
  uint8_t tempbyte;
  
  uint8_t i, j;
  
  i = 0;
  j = num - 1;
  
  while(i < j) {
	  tempbyte = bytes[i];
	  bytes[i] = bytes[j];
	  bytes[j] = tempbyte;

	  j--;
	  i++; 
  }
  
  return;
}
