//#include <stdint.h>
//#include <inttypes.h>
//#include <stdio.h>
//#include <stdlib.h>
#include <avr/io.h>
#include "topology.h"

#include "COM.h"
#include <avr/boot.h>
#include <avr/pgmspace.h>

#include <avr/interrupt.h>

#include <util/delay_basic.h>


//#define nop() __asm volatile ("nop")


//Bootloader takes pages 0..31 -> payload starts at page 32 (which is byte address 0x800)
#define STARTPAGE 32

//payload starts at byte address 0x800
#undef PAYLOAD FLASH_ADDRESS
#define PAYLOAD_FLASH_ADDRESS (0x800)

#undef IS_VECTOR_NODE

void boot_program_page (uint32_t page, uint8_t *buf);


//see boot.h
    void boot_program_page (uint32_t page, uint8_t *buf)
    {
        uint16_t i;

        // Disable interrupts.

        //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.

        // Reenable RWW-section again. We need this if we want to jump back
        // to the application after bootloading.

        //boot_rww_enable ();
	
    }


int main(void)
{
	volatile uint16_t z;
	volatile uint8_t b1;
	//volatile uint8_t direction;
	
	uint8_t b2;
	
	uint8_t pagenumber;
	uint8_t myPage[SPM_PAGESIZE];
	uint8_t bytecounter;
	uint8_t c;
	uint8_t d;
	//uint8_t e;
	uint8_t started;	
	uint8_t retransmitAllowed;
	uint8_t jumperIsSet;

	uint16_t t;
	uint8_t connections;//bit7..0: SNEWSNEW - first nibble says: vector present, second says: normal neighbour present
	
	uint8_t r;
	
	uint8_t loading;
	uint16_t totalbytes;
	void (*ptr_putc) (uint8_t data);
	uint8_t (*ptr_getc) (void);
	
	uint8_t* addressTotalBytesLow = (uint8_t*) EEPROM_VECTOR_ADDRESS+1;
	uint8_t* addressTotalBytesHigh = (uint8_t*) EEPROM_VECTOR_ADDRESS+2;
	
	cli();
	
	jumperIsSet = 1;
	totalbytes = 0;
	connections = 0;
	topology_scan(&connections);//only terminates if no vector is found
	connections = (connections & 0x0F);//delete all vectors
	connections |= (16 << WEST);
		
	
	
	
	loading = 1;
	
	pagenumber = STARTPAGE;
	bytecounter = 0;
		
	
	DDRA = 0xFF;
	PORTA = 0;
	
	
	DDRB &= ~(1 << PB2);//pin 1 is input with pull up
	PORTB |= (1<<PB2);
	
	if((PINB & (1 <<PB2)))
	{
		jumperIsSet = 0;
	}
	
	totalbytes = eeprom_read_byte((uint8_t*)addressTotalBytesHigh) << 8;
	totalbytes |= eeprom_read_byte((uint8_t*)addressTotalBytesLow);

	if(totalbytes > 0xFF00)
	{
		jumperIsSet = 2;
	}
	
	COM_init();
	
	for(r=0; r<SPM_PAGESIZE; r++)
	{
		myPage[r] = 0xFF;
	}

///////////BOOTLOADER
	
	if(jumperIsSet>0)
	{
	RED_ON;
	if(connections >= 16)
	{
		//direction of vector node...
		if( (connections & (16 << WEST)))		
		{
			ptr_putc = LEFT_putc;
			ptr_getc = LEFT_getc;
		}
		else if( (connections & (16 << EAST)))		
		{
			ptr_putc = RIGHT_putc;
			ptr_getc = RIGHT_getc;
		}
		else if( (connections & (16 << NORTH)))		
		{
			ptr_putc = UP_putc;
			ptr_getc = UP_getc;
		}
		else if( (connections & (16 << SOUTH)))		
		{
			ptr_putc = DOWN_putc;
			ptr_getc = DOWN_getc;
		}
		
		pagenumber = STARTPAGE;
		bytecounter = 0;
		totalbytes = 0;
		loading = 1;
		started = 0;
		retransmitAllowed=1;
		
		
		while(loading)
		{
			c  = ptr_getc();
		
			if(c == START)//START
			{
				RED_OFF;//DEBUG
				GREEN_OFF;//DEBUG
				BLUE_OFF;//DEBUG
				bytecounter = 0;
				totalbytes = 0;
				started = 1;
				retransmitAllowed=1;
				pagenumber = STARTPAGE;

			}
			else if(c == END)//END
			{
				if(started)
				{
					if(bytecounter > 0)
					{
						boot_program_page (pagenumber*64, myPage);
					}
				
					for(r=0; r<SPM_PAGESIZE; r++)
					{
						myPage[r] = 0xFF;
					}
	
					loading = 0;
				}
			}
			else if( (c == DATA) || (c == RETRANSMISSION)  )//DATA
			{
				d = ptr_getc();
				if(c == RETRANSMISSION)
				{
					if(retransmitAllowed)
					{
						BLUE_ON;//DEBUG
						if(bytecounter > 0)
						{
							bytecounter--;
						}
					}
				}
				else
				{
					RED_OFF;//DEBUG
					GREEN_OFF;//DEBUG
					BLUE_OFF;//DEBUG
					totalbytes++;
					retransmitAllowed=1;
					
					if(bytecounter == SPM_PAGESIZE)
					{
						boot_program_page (pagenumber*64, myPage);	

						bytecounter = 0;
						pagenumber++;
					
						for(r=0; r<SPM_PAGESIZE; r++)
						{
							myPage[r] = 0xFF;
						}
				}
					
				}
				myPage[bytecounter] = d;
				bytecounter = bytecounter +1;
				
				//_delay_loop_2(800);//PC has hardware UART
				ptr_putc(d);//ACK --> sender sends next byte
				
			}
			else
			{
				RED_ON;//DEBUG
				
				//ERROR
				if(started)
				{
					//SEND WRONG ACK BECAUSE THERE WAS A TRANSMISSION ERROR
					d = ptr_getc();
					_delay_loop_2(200);
					ptr_putc(d+3);//SEND WRONG ACK
					retransmitAllowed=0;
				}
				else
				{
					//IGNORE BYTE
					
				}
			}
		}
		eeprom_write_byte((uint8_t*)addressTotalBytesHigh, (totalbytes&0xFF00)>>8);
		eeprom_write_byte((uint8_t*)addressTotalBytesLow, (totalbytes&0x00FF));

	}
	}
	else//jumper is not set... read totalbytes from eeprom
	{
	}

////////////////////////////////////////////////////////////////////////


_delay_loop_2(62510); // 250ms
_delay_loop_2(62510); // 250ms

	
//////BOOTSENDER	
RED_OFF;
GREEN_OFF;
BLUE_ON;
_delay_loop_2(60000);

				
			connections &= ~(1 << WEST);//PC in the west
			
			
			t = totalbytes + PAYLOAD_FLASH_ADDRESS;//important trick, otherwise compiler "optimizes" incorrect
			totalbytes = t+0;//important trick, otherwise compiler "optimizes" incorrect

	
	
	for(uint8_t volatile direction=0;direction<4;direction++)
	{
		if(direction==EAST)
		{
			ptr_putc = RIGHT_putc;
			ptr_getc = RIGHT_getc;
		}
		else if(direction==WEST)
		{
			ptr_putc = LEFT_putc;
			ptr_getc = LEFT_getc;
		}
		else if(direction==NORTH)
		{
			ptr_putc = UP_putc;
			ptr_getc = UP_getc;
		}
		else if(direction==SOUTH)
		{
			ptr_putc = DOWN_putc;
			ptr_getc = DOWN_getc;
		}
		
		if( (connections & (1 << direction)) && (!(connections & (16 << direction))) )
		{
			//////send block
			ptr_putc(START);
			_delay_loop_2(50); // 1ms
			
			z = PAYLOAD_FLASH_ADDRESS;
			for(z=PAYLOAD_FLASH_ADDRESS;z<totalbytes; z++)
			{
				
				b1 = pgm_read_byte_near(z);				
				//_delay_loop_2(5); // 1ms
				
				ptr_putc(DATA);
				//_delay_loop_2(5); // 1ms
				
				ptr_putc(b1);
				
				b2 = ptr_getc();
				
				
				while(b2 != b1)//byte invalid -> retransmission (error correction)
				{
					_delay_loop_2(50); // 1ms
					ptr_putc(RETRANSMISSION);
					
					_delay_loop_2(50); // 1ms
					
					ptr_putc(b1);
					b2 = ptr_getc();
					
				}
				//_delay_loop_2(5); // 1ms
				
				
			}
			_delay_loop_2(250); // 1ms
			ptr_putc(END);
			
			_delay_loop_2(250); // 1ms
			//////send block
		}
	}
	
	RED_OFF;
	GREEN_OFF;
	BLUE_OFF;
	
	
//////////////////////////////////////////////////////////////////////////////////////////
//now the node blinks, indicating that the process has finished for this node. 
//When every  node blinks (blue or green) the user can restart the nodes
//blue (blinking) nodes must be removed from the luminet, because those are vector nodes
//red blinking indicates that there was an error
//no blinking indicates that there was an error
	while(1)
	{

		if(connections >= 16)//everything was ok, I received code from a vector path
		{
			GREEN_ON;
		}
		else//I did not receive or send anything
		{
			RED_ON;

		}

	}
}



