#include <main.h>

struct work context =
{
   start,			// Actuell menu layer
   start,			// Local menu layer
   start,			// Last menu layer

   NO_EVENT,		// Touch event
   no_event,		// Encoder event
   no_event_flag,	// Event flag

   stop,			// Player in stop mode
   0,				// File at the beginning
   0,0,0,0,0,		// No song data

   "","","","","",	// Error code

   NULL,			// Current directory
   "/",				// Full path to current directory
   NULL,			// Current file
   NULL,			// Full path to current file
   0,				// File counter
   10,				// Directory counter
   1,				// Working in directory

   0,				// Select pointer
   30,				// Start volume
   0, 				// EQ pointer
};

struct info sys_info =
{
   50,
   12.70,
   0,
   08.00,
   "1.0",
   0,
   0,
   0,
   0,
   0,
};

struct qei ENC =
{
   100,
   0,
   0,
   0,
   100,
   0,
   0,
   0,
   0,
   100,
};


void main(void)
{
  init();			 // Call the main init funtion to start the whole system

  while(1)
  {
	  IntMasterEnable(); // Enable Interrupts
	  interruptCtrl();
	  switch (context.event_flag)
	  {
		  case touch_flag:
		  {
			  getTouchData();
			  switchTouchEvent();
		  }
		  	  break;
		  case encoder_flag:
		  {
			  switchENCEvent();
		  }
		  	  break;
		  case timer0_flag:
		  {

		  }
		  	  break;
		  case data_req_flag:
		  {

		  }
		  	  break;
		  default:
			  break;
	  }
	  //IntEnable(INT_GPIOC | INT_GPIOE);
	  context.touch_event = NO_EVENT;
	  context.event_flag = no_event_flag;
	  context.enc_event = no_event;
	}
}

// Main initalisation methode, calls all other init-funktions and configure the basic system register
void init()
{
	// Set clock to 80MHz, with 16MHz chrystall and PLL
	SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
	ROM_FPULazyStackingEnable(); 						// Enable lazy stacking for interrupt handlers.
	ui32Loop = SYSCTL_RCGC2_R;

	// Configure GPIOs
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);

	GPIOIntTypeSet(TOUCH_IRQ_PORT,TOUCH_IRQ_PIN,GPIO_FALLING_EDGE);			// Define T-IRQ for interrupt
	GPIOIntTypeSet(QEI0_BUT_PORT,QEI0_BUT_PIN,GPIO_FALLING_EDGE);			// Define ENCS1 for interrupt
	GPIOIntTypeSet(QEI1_BUT_PORT,QEI1_BUT_PIN,GPIO_FALLING_EDGE);			// Define ENCS2 for interrupt
	GPIOIntTypeSet(VS_DRQ_BASE,VS_DRQ,GPIO_FALLING_EDGE);					// Define Data-RQS for interrupt

	GPIOIntEnable(TOUCH_IRQ_PORT, TOUCH_IRQ_PIN);							// Enable port interrupts for TOUCH_IRQ
	GPIOIntEnable(QEI0_BUT_PORT, QEI0_BUT_PIN);
	GPIOIntEnable(QEI1_BUT_PORT, QEI1_BUT_PIN);
	GPIOIntEnable(VS_DRQ_BASE,VS_DRQ);

	GPIOIntClear(TOUCH_IRQ_PORT,TOUCH_IRQ_PIN);								// Clear previews interrupts; T-IRQ
	GPIOIntClear(QEI0_BUT_PORT,QEI0_BUT_PIN);								// ENCS0 and ENCS1
	GPIOIntClear(QEI1_BUT_PORT,QEI1_BUT_PIN);								// ENCS0 and ENCS1
	GPIOIntClear(VS_DRQ_BASE,VS_DRQ);									    // Data-RQS

	GPIODirModeSet(GPIO_PORTA_BASE,GPIO_PIN_6,GPIO_DIR_MODE_OUT);
	GPIOPinWrite(GPIO_PORTA_BASE,GPIO_PIN_6,GPIO_PIN_6);

	SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);	 		// Enable PWM1 generator
	SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);	// Enable hibernate modul

	//Configure Timer_0_A and Timer_1_A
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);			// Enabler timer 0
	TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);		// Timer counts down
	TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet()*5);	// Timer 0, modul A, 5sec
	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);		// Interrupt for timeout
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
	TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);		// Timer counts down
	TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet()*5);	// Timer 0, modul A, 5sec
	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);		// Interrupt for timeout

	//HibernateWakeSet(HIBERNATE_WAKE_PIN);				// Wake Up from dedicated pin
	//HibernateIntEnable(HIBERNATE_INT_PIN_WAKE);			// Enable interrupt for wake pin

	SysTickIntEnable();						// Enable interrupts for SysTick modul
	interruptCtrl();	// Enable the interrupts
	setBrightness();	// Enable backlight with 50% brightness
	LCDinit();			// Init LCD display
	QEIinit();			// Init encoder interfaces
	Touchinit();		// Init touch controller
	SDinit();			// Init SD Card and file system
	//I2Cinit();			// Init I²C interface
	VS1063init();		// Init VS1063 chip
	printScreen();		// Print start screen
}

// Method is called in main if a touch event occurse and simplified the main routine
void switchTouchEvent()
{
	switch (context.touch_event)
	{
	 	 case BUT_PLAYER:
	 	 {
	 		 context.last_menu = context.cur_menu;		// Save current menu layer
			 context.cur_menu = select;					// Set current menu layer to select
			 printScreen();								// Update screen
	 	 }
	 	 	 break;
	 	 case BUT_OFF:
	 	 {
	 		 HibernateRequest();
	 	 }
	 	 	 break;
	 	 case BUT_SETUP:
	 	 {
			 context.last_menu = context.cur_menu;		// Save current menu layer
			 context.cur_menu = setup;					// Set current menu layer to setup
			 printScreen();								// Update screen
	 	 }
			  break;
	 	 case BUT_EQ:
	 	 {
			 context.last_menu = context.cur_menu;
			 context.cur_menu= eq;
			 printScreen();
	 	 }
			 break;
		 case BUT_BACK:
		 {
			 context.loc_menu = context.cur_menu;
			 context.cur_menu = context.last_menu;
			 context.last_menu = context.loc_menu;
			 printScreen();
		 }
			 break;
		 case BUT_ENTER:
		 {
			  context.loc_menu = context.cur_menu;
			  context.cur_menu = context.last_menu;
			  context.last_menu = context.loc_menu;
			  printScreen();
		 }
			 break;
		case BUT_PP:
		{
			if(context.play_state == play) context.play_state = pause;
			else context.play_state = play;
		}
			 break;
		case NO_EVENT:
		{

		}
			 break;
		default:
			 break;
	}
}

void switchENCEvent()
{
	switch(context.enc_event)
	{
		case push1:
		{
			switch(context.cur_menu)
			{
				case select:
				{

				}
					break;
			}
		}
			break;
		case push0:
		{
			switch(context.cur_menu)
			{
			case start:
			{
				context.cur_menu = select;
				context.last_menu= start;
				printScreen();
			}
			break;
				case select:
				{
					context.cur_dir = dir_list[context.sel_pointer];						// Save selectet directory
										context.dir_path = strcat(context.dir_path,context.cur_dir);	// Save full path name for the directory
										enterDir(context.cur_dir);
										printScreen();
				}
					break;
				case player:
				{
					if(context.play_state == play) context.play_state = pause;
					else context.play_state = play;
				}
					break;
				case setup:
				{

				}
					break;
				case eq:
				{

				}
					break;
				case eq_bar:
				{

				}
					break;
				default:
					break;
			}
		}
			break;
		case down0:
		{
			switch(context.cur_menu)
			{
				case select:
				{
					// If the printest directory oder file list is at the beginning switch to it's end and reprint the screen
					if(context.sel_pointer == 0)
					{
						if(context.isDir) context.sel_pointer = context.dir_count-1;
						else context.sel_pointer = context.file_count-1;
						printScreen();
					}
					// 20 names are on the display so if we are at the beginning print the previevs 20 and the SelectBar jumps to the bottom and reprint scrren
					else if(context.sel_pointer % 20 == 0)	printScreen();
					context.sel_pointer -= 1;		// Decreas select pointer by one

					setSelectBar();						// print selectBar
				}
					break;
				case player:
				{

				}
					break;
				case setup:
				{

				}
					break;
				case eq:
				{
					switch(context.eq_pointer)
					{
					case 0: if((sys_info.EQ1_lvl- ENC.ENC0_DOWN )>-32) sys_info.EQ1_lvl -= ENC.ENC0_DOWN;
						break;
					case 1: if((sys_info.EQ2_lvl- ENC.ENC0_DOWN )>-32) sys_info.EQ2_lvl -= ENC.ENC0_DOWN;
						break;
					case 2: if((sys_info.EQ3_lvl- ENC.ENC0_DOWN )>-32) sys_info.EQ3_lvl -= ENC.ENC0_DOWN;
						break;
					case 3: if((sys_info.EQ4_lvl- ENC.ENC0_DOWN )>-32) sys_info.EQ4_lvl -= ENC.ENC0_DOWN;
						break;
					case 4: if((sys_info.EQ5_lvl- ENC.ENC0_DOWN )>-32) sys_info.EQ5_lvl -= ENC.ENC0_DOWN;
						break;
					}
				}
					break;
				case eq_bar:
				{

				}
					break;
				default:
					break;
			}
		}
		case up0:
		{
			switch(context.cur_menu)
			{
				case select:
				{

					// Reprint screen if the send of the data list is reached and reset the select pointer
					// 0 <= sel_pointer <= dir/file_count-1, because dir/file_count start at 1
					if(context.sel_pointer == context.dir_count-1 || context.sel_pointer == context.file_count-1)
					{
						context.sel_pointer = 0;
						printScreen();
					}
					// Reprint screen if the select bar is at the end of the screeen
					else if(context.sel_pointer >20 && context.sel_pointer % 20 == 1) printScreen();
					context.sel_pointer += 1; 		    // Increment counter
					setSelectBar();						// print selectBar
				}
					break;
				case player:
				{

				}
					break;
				case setup:
				{

				}
					break;
				case eq:
				{
					switch(context.eq_pointer)
					{
					case 0: if((sys_info.EQ1_lvl+ ENC.ENC0_UP)<32) sys_info.EQ1_lvl += ENC.ENC0_UP;
						break;
					case 1: if((sys_info.EQ2_lvl+ ENC.ENC0_UP)<32) sys_info.EQ2_lvl += ENC.ENC0_UP;
						break;
					case 2: if((sys_info.EQ3_lvl+ ENC.ENC0_UP)<32) sys_info.EQ3_lvl += ENC.ENC0_UP;
						break;
					case 3: if((sys_info.EQ4_lvl+ ENC.ENC0_UP)<32) sys_info.EQ4_lvl += ENC.ENC0_UP;
						break;
					case 4: if((sys_info.EQ5_lvl+ ENC.ENC0_UP)<32) sys_info.EQ5_lvl += ENC.ENC0_UP;
						break;
					}
				}
					break;
				case eq_bar:
				{

				}
					break;
				default:
					break;
			}
		}
	}
}
// Enable all interrupts for the system
// If an event occured disable further interrupts for it
void interruptCtrl()
{
	IntEnable(TOUCH_IRQ_INTR);				// Enable interrupts for GPIO port C
	IntEnable(QEI0_BUT_INTR);				// Enable interrupts for GPIO ports H
	IntEnable(VS_DRQ_INTR);					// Enable interrupts for GPIO port G
	IntEnable(INT_QEI0);					// Enable interrupts for QEI0
	IntEnable(INT_QEI1);					// Enable interrupts for QEI1
	IntEnable(INT_HIBERNATE);				// Enable interrupts for hibernation modul
	IntEnable(INT_WATCHDOG);				// Enable interrupts for watchdog
	IntEnable(INT_I2C0);					// Enable interrupts for I²C modul 0
	IntEnable(INT_I2C3);					// Enable interrupts for I²C modul 3
	IntEnable(INT_TIMER0A);
	GPIOIntClear(TOUCH_IRQ_PORT,TOUCH_IRQ_PIN);								// Clear previews interrupts; T-IRQ
	GPIOIntClear(QEI0_BUT_PORT,QEI0_BUT_PIN);								// ENCS0 and ENCS1
	GPIOIntClear(QEI1_BUT_PORT,QEI1_BUT_PIN);								// ENCS0 and ENCS1
	GPIOIntClear(VS_DRQ_BASE,VS_DRQ);									    // Data-RQS
	QEIIntClear(QEI0_BASE,QEI_INTDIR);
	QEIIntClear(QEI1_BASE,QEI_INTDIR);
	switch(context.event_flag)
	{
		case encoder_flag:
		{
			if(context.enc_event == up0 || context.enc_event == down0)	IntDisable(INT_QEI0);
			else IntDisable(INT_QEI1);
		}
			break;
		case touch_flag:
		{
			IntDisable(TOUCH_IRQ_INTR);
		}
			break;
		case data_req_flag:
		{
			IntDisable(VS_DRQ_INTR);
		}
		default:
			break;
	}
}

// Timer 0 counts down for 5 sec. If an operation takes more timer something is not okay so there will be a system reset
void watchDog(bool set)
{
	if(set) TimerEnable(TIMER0_BASE, TIMER_A);	 // Enable Timer_0_A
	else 	TimerDisable(TIMER0_BASE, TIMER_A);	 // Disable TIMER_0_A
}
