#define F_CPU 16000000UL
#define  __APP_Major__	1
#define  __APP_Minor__	0
//#define  __APP_Build__	33

//Насраиваем параметры для функции вывода
#define StringFunction_PutChar sendchar

#include "defines.h"
#include "serial.h"
#include "flash.h"
#include "C:\Users\Faster\Documents\Libs\AVR\FS_StringFunctions.cpp"
//#include "C:\Users\Faster\Documents\Libs\AVR\FS_UART.cpp"
#include <util/delay.h>

/* Uncomment the following to save code space */
//#define REMOVE_AVRPROG_SUPPORT
//#define REMOVE_FUSE_AND_LOCK_BIT_SUPPORT
//#define REMOVE_BLOCK_SUPPORT
//#define REMOVE_EEPROM_BYTE_SUPPORT
//#define REMOVE_FLASH_BYTE_SUPPORT
void boot_program_page (WORD page, BYTE *buf){
	WORD i;
	BYTE sreg;
	// запрещаем прерывания
	sreg = SREG;
	asm("CLI");
	eeprom_busy_wait(); // ожидаем, завершения предыдущей записи
	boot_page_erase_safe (page);// стираем указанную страницу
	boot_spm_busy_wait(); // ожидаем завершения стирания
	// цикл заполнения страницы памяти
	for (i=0; i<SPM_PAGESIZE; i+=2){
		// заполняем словами «в обратном порядке»
		uint16_t w = *buf++;
		w += (*buf++) << 8;
		boot_page_fill_safe (page + i, w);
	}
	boot_page_write_safe (page);// записываем заполненную страницу
	boot_spm_busy_wait(); // ожидаем завершения записи
	// разрешение перехода к RWWсекции памяти. Это нужно, если
	// после самозаписи должен быть осуществлен переход к основной программе
	boot_rww_enable();
	// восстановление состояния прерываний, как было при входе в функцию
	SREG = sreg;
}
/*
 * GCC doesn't optimize long int arithmetics very clever.  As the
 * address only needs to be larger than 16 bits for the ATmega128 and
 * above (where flash consumptions isn't much of an issue as the
 * entire boot loader will still fit even into the smallest possible
 * boot loader section), save space by using a 16-bit variable for the
 * smaller devices.
 */
#ifdef LARGE_MEMORY
#  define ADDR_T unsigned long
#else  /* !LARGE_MEMORY */
#  define ADDR_T unsigned int
#endif /* LARGE_MEMORY */

#ifndef REMOVE_BLOCK_SUPPORT
unsigned char BlockLoad(unsigned int size, unsigned char mem, ADDR_T *address);
void BlockRead(unsigned int size, unsigned char mem, ADDR_T *address);

/* BLOCKSIZE should be chosen so that the following holds: BLOCKSIZE*n = PAGESIZE,  where n=1,2,3... */
#define BLOCKSIZE PAGESIZE

#endif /* REMOVE_BLOCK_SUPPORT */

//-------------------------------------------------------------------------------------------
//Текстовые строки
const BYTE CRLF[] PROGMEM = "\r\n\0";
const BYTE Caption[] PROGMEM =	"\a\r\nFastSoft Boot Console v\0";
const BYTE MCU[] PROGMEM="\r\nMCU SIGN #\0";
const BYTE HELP[] PROGMEM="\r\nType:\r\nc - To entry in the console mode\r\nu - To update FirmWare\r\nr - To restart (automatically after 30 seconds)\0";
const BYTE CHOICE[] PROGMEM="\r\nChoice>\0";
const BYTE CONSOLE[] PROGMEM="\r\nStart console...\0";
const BYTE UPDATE[] PROGMEM="\r\nStarting FirmWare updating\r\nWaiting  data\r\n\a\0";
const BYTE RESTART[] PROGMEM="\r\nRestart...\0";
const BYTE UNKNOWN[] PROGMEM="\r\nUnknown command, seconds before the end:\0";


int main(void)
{
    unsigned int temp_int;
    unsigned char val;
	

    /* Initialization */
	  
    static void (*StartProg)( void ) = 0x0000; // Set up function pointer to RESET vector.
    initbootuart(); // Initialize UART.

	//Инициализация портов для нужд BootLoader'а
	asm("CLI");
	PORTD=0x04;
	DDRD=0xF8;

	
	//Инициализируем индикатор
	ResBit(PORTD,3);	//Зел- выкл
	SetBit(PORTD,4);	//Красн - вкл
	
	_delay_ms(1000);	//Ждем секунду, типа грузимся (потом подумать над временем)
	
	ResBit(PORTD,4);	//Отколючаем красный
	
	temp_int=0;
	while ((~PIND & 4) && temp_int<50 )
	{
		//Если зажата кнопка мигаем красным и так не менее 5 секунд с интервалами 0.1 секунды 
		InvertBit(PORTD,4);
		_delay_ms(100);
		temp_int++;
		
	}
	
	ResBit(PORTD,4);	//Выключаем красный

    //Проверяем нужен ли нам загрузчик ?
    if(temp_int==50)//(temp_int==50) 
    {
        //Запуск програматора, зажигаем лампочку желтым цветом
		SetBit(PORTD,3);
		SetBit(PORTD,4);
		
		//Выводим заголовок
		print_pm(Caption);						
		print_u16(__APP_Major__);				//Вывод версии Major
		StringFunction_PutChar('.');			//.
		print_u16(__APP_Minor__);				//Вывод версии Mainor
		//Информация об MCU
		print_pm(MCU);							
		print_u8_hex(SIGNATURE_BYTE_1);			//Выводим сигнатуру MCU              
		print_u8_hex(SIGNATURE_BYTE_2);
		print_u8_hex(SIGNATURE_BYTE_3);
		//Выводим помощь
		print_pm(HELP);
		print_pm(CHOICE);	
			
		//Ждем 30 секунд для ввода комманды
		temp_int=300;
UnknownCMD:;
		while((!(UART_STATUS_REG & (1 << RECEIVE_COMPLETE_BIT))) && --temp_int >0)_delay_ms(100); //Ждем ввода илитайм аута	 
	
		//Проверка, если кончилось время ожидания то по умолчанию 'r'
		if(!temp_int)val='r';
			else val=UART_DATA_REG;
		StringFunction_PutChar(val);//Выводим что ввел юзер
		
		switch(val)
		{
			case 'c'://Console
			case 'C':
			{
				print_pm(CONSOLE);
				SetBit(PORTD,3);	//Зел- вкл
				ResBit(PORTD,4);	//Красн - выкл
				_delay_ms(1000);
				while(1);
				//TODO: Переход на консоль
			}
			case 'u'://Update
			case 'U':
			{
				BYTE Buff[64];
				print_pm(UPDATE);
				ResBit(PORTD,3);	//Зел- выкл
				SetBit(PORTD,4);	//Красн - вкл
				_delay_ms(1000);
				
				//TEST
				
				//Fill mem
				temp_int=64;
				while(--temp_int)Buff[temp_int]=1;
				
				//
				boot_program_page(64,Buff);

				ResBit(PORTD,3);	
				ResBit(PORTD,4);	
				
				while(1);
				//TODO: Переход на обновление прошивки
			}
			case 'r'://Restart
			case 'R': 
			{
				print_pm(RESTART);
				ResBit(PORTD,3);	//Зел- выкл
				ResBit(PORTD,4);	//Красн - выкл
				_delay_ms(1000);
				StartProg();//asm("LDI R30,0x00\r\nLDI R31,0x00\r\nIJMP\r\n");
			}
			default://Если чтото не опнятное
			{
				print_pm(UNKNOWN);
				print_u16(temp_int/10);
				print_pm(CHOICE);
				goto UnknownCMD; 
			}
		}
	}
	else 
	{
		SetBit(PORTD,3);	 
		ResBit(PORTD,4);	
		_delay_ms(1000);
		boot_rww_enable();
		StartProg();//asm("LDI R30,0x00\r\nLDI R31,0x00\r\nIJMP\r\n");
	}
}
