/* Copyright (c) 2011 Axel Wachtler, Daniel Thiele
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 * 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.
 * Neither the name of the authors 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */

/* $Id$ */
/**
 * @file
 * @brief ....
 * @_addtogroup grpApp...
 *
 * Sensor Type			Part Name	I2C address
 * --------------------------------------------
 * Acceleration			LIS331HH	0x32
 * Magnetic				HMC5883L	0x3C
 * Pressure				BMP085		0xEE
 *
 *
 * Flash Usage
 * -------------------------------------------
 *
 * ATmega328P, Pagesize=128
 *
 * 0x0000
 * ... PROGRAM (6144 Bytes, 48 pages)
 * 0x0BFF
 * 0x0C00
 * ... DATA STORAGE (24576 Bytes, 192 pages)
 * 0x3BFF
 * 0x3C00
 * ... BOOTLOADER (1024 Words, 16 pages)
 * 0x3FFF
 *
 *
 * Data Storage in Flash
 * -------------------------------------------
 * Samplerate=10ms, Sampling=5min=300sec=30000samples
 *
 * 16 Bytes per Sample (flash_chunk_t)
 * available storage size: 24576 Bytes (defined above) = 1536samples
 * Sampling 5min, Samplerate=1536samples/5min = 5samples/second =200ms
 *
 *
 *
 */

/* === includes ============================================================ */

/* avr-libc inclusions */
#include <string.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <avr/boot.h>

/* uracoli inclusions */
#include <ioutil.h>
#include <timer.h>
#include <hif.h>
#include <transceiver.h>
#include <radio.h>

/* application inclusions */
#include "i2c.h"
#include "sensors.h"
#include "rose.h"

#if defined(SELFTEST)
#include "selftest.h"
#endif

/* === macros ============================================================== */

#define GET_CFG_ADDR() (0xfffc)

#define TXPERIOD_SLOW MSEC(1000)
#define TXPERIOD_FAST MSEC(10)
#define NBSAMPLES (20)	/* number of samples for fast stream mode */

#define FLASHSTORE_SIZE (SPM_PAGESIZE*192) /* partitioning above */
#define FLASHSTORE_EACHSAMPLE (20) /* number of samples until flash store */

/* === types =============================================================== */

typedef enum {
	APP_IDLE, APP_MEASURE, APP_RESULT, APP_READFLASH,
} app_event_t;

typedef struct{
	uint8_t pagebuf[SPM_PAGESIZE];	/* buffer for writing flash pages */
	uint32_t flashidx; /* current idx of flash address, used for read and write */
	uint8_t pagebufidx;	/* current idx for filling pagebuf */
	uint8_t samplecnt; /* counting measurement samples until storage */
}flash_storage_t;

/* === globals ============================================================= */
static volatile time_t txperiod_msec = TXPERIOD_SLOW;

static volatile timer_hdl_t thdl_tx;

static const uint32_t flashstore_baseaddr = 0x1800; /* 0x0C00 word-addres as byte address */
static const uint32_t flashstore_endaddr =  0x7800; /* start of bootloader section as byte address */
static volatile uint8_t storetoflash = 0;
static flash_storage_t flashstore;

static rose_slowframe_t slowfrm = {
		.hdr.FCF = 0x8861, /* data frame, panid compr, ACK requested */
		.hdr.boardtype = BOARD_ROSE231,
		.hdr.frametype = SENSORPROTO_FRAMETYPE_ROSE_SLOW,
		.version_maj=0, .version_min=3
	};

static rose_fastframe_t fastfrm = {
		.hdr.FCF = 0x8841, /* data frame, panid compr, no ACK requested */
		.hdr.boardtype = BOARD_ROSE231,
		.hdr.frametype = SENSORPROTO_FRAMETYPE_ROSE_FAST
	};

volatile app_event_t app_event = APP_IDLE;
static volatile rose_appmode_t appmode = ROSE_APPMODE_SLOW;
static uint16_t fastcnt = 0;

/* === prototypes ========================================================== */

/* function must reside in bootloader section to enable writing to flash */
void boot_program_page (uint32_t page, uint8_t *buf) __attribute__ ((section (".bootloader")));
void flash_store(rose_measresult_t *res);

/* === functions =========================================================== */

#if defined(rose231)
static inline void trigger() {
	sensors_bmp085_up_trigger();
	sensors_lis331_trigger();
	sensors_hmc5883l_trigger();
}

static inline void readresults(rose_measresult_t *res) {
	res->pressure = sensors_bmp085_readresult();
	sensors_lis331hh_readresult(&res->acc);
	sensors_hmc5883l_readresult(&res->comp);
}

#endif /* defined(rose231) */

void boot_program_page (uint32_t page, uint8_t *buf)
 {
     uint16_t i;
     uint8_t sreg;

     sreg = SREG;
     cli();

     eeprom_busy_wait ();

     boot_page_erase (page);
     boot_spm_busy_wait ();      // Wait until the memory is erased.

     for (i=0; i<SPM_PAGESIZE; i+=2)
     {
         // Set up little-endian word.

         uint16_t w = *buf++;
         w += (*buf++) << 8;

         boot_page_fill (page + i, w);
     }

     boot_page_write (page);     // Store buffer in flash page.
     boot_spm_busy_wait();       // Wait until the memory is written.

     boot_rww_enable ();

     SREG = sreg;
 }

/*
 * \brief Store measurement set to flash
 */
void flash_store(rose_measresult_t *res)
{
	static rose_flashchunk_t chunk;

	if(++flashstore.samplecnt >= FLASHSTORE_EACHSAMPLE){
		flashstore.samplecnt = 0;

		chunk.tstamp = res->tstamp;
		chunk.pressure = res->pressure;
		memcpy(&chunk.acc, &res->acc, sizeof(sensors_xyz_result_t));
		memcpy(&chunk.comp, &res->comp, sizeof(sensors_xyz_result_t));

		memcpy(&flashstore.pagebuf[flashstore.pagebufidx], &chunk, sizeof(rose_flashchunk_t));

		flashstore.pagebufidx += sizeof(rose_flashchunk_t);
		if(flashstore.pagebufidx >= SPM_PAGESIZE){
			flashstore.pagebufidx = 0;
			boot_program_page(flashstore.flashidx, flashstore.pagebuf);
			flashstore.flashidx += SPM_PAGESIZE;
			if(flashstore.flashidx >= flashstore_endaddr){
				storetoflash = 0;	/* stop */
			}
		} /* if(flashstore.pagebufidx >= SPM_PAGESIZE) */
	} /* if(++flashstore.samplecnt >= FLASHSTORE_EACHSAMPLE) */
}

/*
 * \brief Initialize Application
 */
static void app_init(void)
{
	LED_INIT();
	i2c_init();
	hif_init(9600);
	timer_init();
	sensorproto_init(BOARD_ROSE231);
	sensors_init();

	slowfrm.hdr.destpanid = sensorproto_get_config()->pan_id;
	slowfrm.hdr.srcaddr = sensorproto_get_config()->short_addr;

	fastfrm.hdr.destpanid = sensorproto_get_config()->pan_id;
	fastfrm.hdr.srcaddr = sensorproto_get_config()->short_addr;

	sei();
}

static void app_task(void) {
	app_event_t state;

	uint8_t *datatosend;
	uint8_t sizetosend;

	cli();
	state = app_event;
	app_event = APP_IDLE;
	sei();

	do {
		switch (state) {
		case APP_IDLE:
			break;
		case APP_MEASURE:
			if (ROSE_APPMODE_FAST == appmode) {
				fastfrm.data.tstamp += txperiod_msec;

				if(NBSAMPLES == fastcnt){
					readresults(&fastfrm.data); /* get results from previous slot */
				}else{
					/* first slot is invalid because of pipelined measurement */
					memset(&fastfrm.data, 0x00, sizeof(rose_measresult_t));
				}
				trigger(); /* and trigger next meas */
			} else {
				slowfrm.data.tstamp += txperiod_msec;
				trigger();
				_delay_ms(12);
				readresults(&slowfrm.data);

				/* additional measurement of temperature */
				if(ROSE_APPMODE_SLOW == appmode){
					sensors_bmp085_ut_trigger();
					_delay_ms(5);
					uint8_t tmp[2];
					i2c_read(SENSORS_I2CADDR_BMP085, RG_BMP085_ADC_OUT_MSB_REG, tmp, 2);
					slowfrm.data.temperature = tmp[0]<<8 | tmp[1];
				}
				sensors_lis331_powerdown();
				sensors_bmp085_readee(slowfrm.bmp085prom);
				slowfrm.vbat_mv = measure_vmcu();
			}
			if ((ROSE_APPMODE_FAST == appmode) || (ROSE_APPMODE_READFLASH == appmode)) {
				datatosend = (uint8_t *) &fastfrm;
				sizetosend = sizeof(fastfrm);
			} else {
				datatosend = (uint8_t *) &slowfrm;
				sizetosend = sizeof(slowfrm);
			}
			state = APP_RESULT;
			break;
		case APP_READFLASH:
			memcpy_P((void*)&fastfrm.data, (PGM_VOID_P)flashstore.flashidx, sizeof(rose_measresult_t));
			flashstore.flashidx += sizeof(rose_flashchunk_t);
			state = APP_RESULT;
			break;
		case APP_RESULT:
			/* take care not to exceed the limit of TxFrame.data */
			radio_set_state(STATE_TXAUTO);
			radio_send_frame(sizetosend, datatosend, 0);

			/* store during sending frame */
			if( (ROSE_APPMODE_FAST == appmode) && storetoflash){
				flash_store(&fastfrm.data);
			}

			state = APP_IDLE;
			break;
		default:
			state = APP_IDLE;
			break;
		}
	} while (state != APP_IDLE);
}

static inline void do_sleep(void) {
	set_sleep_mode(SLEEP_MODE_IDLE);
	sleep_mode();
}

time_t tmr_transmit(timer_arg_t t) {
	if(ROSE_APPMODE_READFLASH == appmode){
		app_event = APP_READFLASH;
	}else{
		app_event = APP_MEASURE;
	}

	/* periodic timer */
	return txperiod_msec;
}

static inline void app_setmode(rose_appmode_t mode) {
	switch (mode) {
	case ROSE_APPMODE_SLOW:
		storetoflash = 0;
		appmode = ROSE_APPMODE_SLOW;
		txperiod_msec = TXPERIOD_SLOW;
		radio_set_param(RP_IDLESTATE(STATE_RXAUTO));	/* for the Rx slot */
		trx_bit_write(SR_MAX_CSMA_RETRES, 5);
		break;
	case ROSE_APPMODE_FAST_FLASH:
		flashstore.flashidx = flashstore_baseaddr;
		flashstore.samplecnt = 0;
	case ROSE_APPMODE_FAST:			/* fall through, same behaviour for APPMODE_FAST_FLASH */
		appmode = ROSE_APPMODE_FAST;
		txperiod_msec = TXPERIOD_FAST;
		radio_set_param(RP_IDLESTATE(STATE_OFF));
		trx_bit_write(SR_MAX_CSMA_RETRES, 7); /* no CSMA, immediate transmitting */
		fastcnt = NBSAMPLES;
		break;
	case ROSE_APPMODE_READFLASH:
		storetoflash = 0;
		appmode = ROSE_APPMODE_READFLASH;
		txperiod_msec = TXPERIOD_FAST;
		radio_set_param(RP_IDLESTATE(STATE_OFF));
		trx_bit_write(SR_MAX_CSMA_RETRES, 7); /* no CSMA, immediate transmitting */
		flashstore.flashidx = flashstore_baseaddr;
		fastcnt = NBSAMPLES / FLASHSTORE_EACHSAMPLE;
		break;
	default:
		break;
	} /* switch (mode) */

	if(ROSE_APPMODE_FAST_FLASH == mode){
		storetoflash = 1;
	}else{
		storetoflash = 0;
	}
}

void cb_sensorproto_rx(uint8_t *frm)
{
	rose_cfgframe_t *rosecfg = (rose_cfgframe_t*) frm;
	if (SENSORPROTO_FRAMETYPE_ROSE_CFG == rosecfg->hdr.frametype) {
		app_setmode(rosecfg->appmode);
	}
}

void cb_sensorproto_txdone(radio_tx_done_t status)
{
	if (TX_OK == status) {
		slowfrm.hdr.seqnumber++;
		fastfrm.hdr.seqnumber++;
	}

	if (ROSE_APPMODE_SLOW == appmode) {
	} else if ( (ROSE_APPMODE_FAST == appmode) || (ROSE_APPMODE_READFLASH == appmode)){
		/* do nothing, STATE_OFF is done via IDLESTATE */
		if (0 == --fastcnt) {
			app_setmode(ROSE_APPMODE_SLOW);
		}
	}
}

int main() {
	MCUSR = 0;
	wdt_disable();

#if defined(SELFTEST)
	selftest();
#endif

	app_init();
	app_setmode(ROSE_APPMODE_SLOW);
	thdl_tx = timer_start(tmr_transmit, txperiod_msec, 0);
	for (;;) {
		app_task();
		/** sleep, node awakes by an occuring IRQ. */
		do_sleep();
	}
}

/* EOF */
