#include "main.h"

extern struct work context;

FRESULT iFResult;						// Result from FF file system

void VS1063init()
{
	// Configure SSI
	SysCtlPeripheralEnable(VS_SSI_PER); 															// Enable clock for SSI modul
	//GPIOPinConfigure(GPIO_PK2_SSI1RX);
	//GPIOPinConfigure(GPIO_PK3_SSI1TX);
	//GPIOPinConfigure(GPIO_PK0_SSI1CLK);
	GPIOPinTypeSSI(VS_SSI_PORT,VS_CLK|VS_MOSI|VS_MISO);												// Select SSI pins for AF
	GPIOPadConfigSet(VS_SSI_PORT, VS_MISO, GPIO_STRENGTH_6MA, GPIO_PIN_TYPE_STD_WPU);				// Set weak pull up on RX
	GPIOPadConfigSet(VS_SSI_PORT, VS_CLK | VS_MOSI, GPIO_STRENGTH_6MA, GPIO_PIN_TYPE_STD);			// All other are push pull
	// Clock from CPU, mastermode, 10kHz, 8bit, capture on rising edge, clock is normal low
	SSIConfigSetExpClk(VS_SSI_BASE,SysCtlClockGet(),SSI_FRF_MOTO_MODE_0,SSI_MODE_MASTER,10000,8);
	SSIEnable(VS_SSI_BASE);																			// Enable SSI modul

	// Configure controll pins
	GPIOPinTypeGPIOOutput(VS_FCS_BASE, VS_FCS);										// Config SSI CS as an output
	GPIOPinTypeGPIOOutput(VS_DCS_BASE, VS_DCS);										// Config VS_DCS as an output
	GPIOPinTypeGPIOInput(VS_DRQ_BASE, VS_DRQ);										// Config VS_VS_DRQ as an input
	GPIOPinTypeGPIOOutput(VS_RST_BASE, VS_RST);									// Config VS_RST as an output
	GPIOPinWrite(VS_FCS_BASE, VS_FCS, VS_FCS);											// Set SSI CS high
	GPIOPinWrite(VS_DCS_BASE, VS_DCS, VS_DCS);											// Set VS_FCS high
	GPIOPinWrite(VS_RST_BASE, VS_RST, VS_RST);									// Set VS_RST high
	GPIOPadConfigSet(VS_FCS_BASE,VS_FCS,GPIO_STRENGTH_6MA,GPIO_PIN_TYPE_STD);			// 6mA push-pull output
	GPIOPadConfigSet(VS_DCS_BASE,VS_DCS,GPIO_STRENGTH_6MA,GPIO_PIN_TYPE_STD);			// 6mA push-pull output
	GPIOPadConfigSet(VS_RST_BASE,VS_RST,GPIO_STRENGTH_6MA,GPIO_PIN_TYPE_STD);	// 6mA push-pull output

	VS1063readData(SCI_MODE);								// Dummy read to set SPI comand bus to a known state
	VS1063sendCMD(SCI_MODE, (SM_SDINEW|SM_TESTS|SM_RESET));	// Software reset, allow SDI test, normal SPI mode
	VS1063sendCMD(SCI_HDAT0, 0xABAD);						// Test write 1
	VS1063sendCMD(SCI_HDAT1, 0x1DEA);						// Test write 2
	// If the registers didn't contain the same value something is wrong
	if (VS1063readData(SCI_HDAT0) != 0xABAD || VS1063readData(SCI_HDAT1) != 0x1DEA)
	{
	    context.error.VS_error = "Decoder konnte nicht gestartet werden !" ;
	    return;
	}

	// Cyrstal frequenzy 12,288MHz, System clock x 4, Add maximal 1,5x to playspeed
	VS1063sendCMD(SCI_CLOCKF,HZ_TO_SC_FREQ(12288000) | SC_MULT0 | SC_MULT2 | SC_ADD1);
	// Update SPI speed
	SSIConfigSetExpClk(VS_SSI_BASE,80000000,SSI_FRF_MOTO_MODE_0,SSI_MODE_MASTER,1000000,8);
	// Set Configure Register, disable FLAC decoding
	VS1063sendParam(PAR_CONFIG1, PAR_CONFIG1_DIS_FLAC);
	VS1063sendCMD(SCI_VOL, 0x0c0c);											// Set volume

	LoadPlugIn();															// Load the patches and plugins
}

void VS1063sendCMD(int reg, int cmd)
{
	GPIOPinWrite(VS_DCS_BASE, VS_DCS, 0);							// Select VS_DCS to send a comand
	SSIDataPut(VS_SSI_BASE,0x0002);							// Tell VS1063 we want to write some data
	while(SSIBusy(VS_SSI_BASE)) ;							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,reg);							// Send the register adress we want to write
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,(cmd & 0xFF00) < 8);				// Send upper 8 bit of comand
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,(cmd & 0x00FF));					// Send lower 8 bit of comand
	GPIOPinWrite(VS_DCS_BASE, VS_DCS, VS_DCS);						// Deselect DCS to send a comand
	watchDog(true);											// Enable WatchDog
	while(!(GPIOPinRead(VS_DRQ_BASE,VS_DRQ & VS_DRQ)));				// Wait till VS_DRQ is high and VS1063 is ready
	watchDog(false);										// Disable WatchDog
}

void VS1063sendData(int reg, int data)
{
	GPIOPinWrite(VS_DCS_BASE, VS_FCS, 0);							// Select VS_FCS to send a comand
	SSIDataPut(VS_SSI_BASE,0x0002);							// Tell VS1063 we want to write some data
	while(SSIBusy(VS_SSI_BASE)) ;							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,reg);							// Send the register adress we want to write
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,(data & 0xFF00) < 8);			// Send upper 8 bit of data
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,(data & 0x00FF));				// Send lower 8 bit of data
	GPIOPinWrite(VS_FCS_BASE, VS_FCS, VS_FCS);						// Deselect VS_FCS to send a comand
	watchDog(true);											// Enable WatchDog
	while(!(GPIOPinRead(VS_DRQ_BASE,VS_DRQ & VS_DRQ)));				// Wait till VS_DRQ is high and VS1063 is ready
	watchDog(false);										// Disable WatchDog
}
void VS1063sendParam(int reg, int param)
{
	VS1063sendCMD(SCI_WRAMADDR,reg);						// Set parameter register adress in user code register
	VS1063sendCMD(SCI_WRAM,param);							// Update parameter value in WRAM register
}

int VS1063readData(int reg)
{
	unsigned int locBuffer = 0;								// Buffer for lower 8 bit
	unsigned int locBuffer1= 0;								// Buffer for higher 8 bit
	GPIOPinWrite(VS_DCS_BASE, VS_DCS, 0);							// Select VS_DCS to send a comand
	SSIDataPut(VS_SSI_BASE,0x0003);							// Tell VS1063 we want to read some data
	while(SSIBusy(VS_SSI_BASE)) ;							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,reg);							// Send the register adress we want to write
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,0);								// Send dummy byte to read FIFO
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataGet(VS_SSI_BASE,&locBuffer);						// Receive upper 8 bit
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataPut(VS_SSI_BASE,0);								// Send ldummy byte to read FIFO
	while(SSIBusy(VS_SSI_BASE));							// Wait untill SSI is ready again
	SSIDataGet(VS_SSI_BASE,&locBuffer1);					// Receive lower 8 bit
	ROM_SysCtlDelay(100);										// Wait for VS1063 to be ready again
	GPIOPinWrite(VS_DCS_BASE, VS_DCS, VS_DCS);						// Select VS_DCS to send a comand
	return (locBuffer<8 + locBuffer1);						// Return received
}

// Loads a plugin or patch of VLSI
void LoadPlugIn()
{
	int len = sizeof(plugin)/sizeof(plugin[0]);
	int i = 0;
	while (i<len)
	{
		unsigned short addr, n, val;
		addr = plugin[i++];
		n = plugin[i++];
		if (n & 0x8000U)
		{ /* RLE run, replicate n samples */
			n &= 0x7FFF;
			val = plugin[i++];
			while (n--)
			{
				VS1063sendCMD(addr, val);
			}
		}
		else
		{           /* Copy run, copy n samples */
			while (n--)
			{
				val = plugin[i++];
				VS1063sendCMD(addr, val);
			}
		}
	}
}

/*

  This function plays back an audio file.

  It also contains a simple user interface, which requires the following
  funtions that you must provide:
  void SaveUIState(void);
  - saves the user interface state and sets the system up
  - may in many cases be implemented as an empty function
  void RestoreUIState(void);
  - Restores user interface state before exit
  - may in many cases be implemented as an empty function
  int GetUICommand(void);
  - Returns -1 for no operation
  - Returns -2 for cancel playback command
  - Returns any other for user input. For supported commands, see code.

*/
void VS1063PlayFile()
{
  unsigned int file_pos=0;              			// File position
  int endFillByte = 0;        						// What byte value to send after file
  //int playMode = VS1063readData(PAR_PLAY_MODE);		// Get the current play mode
  int playerPause = 0;
  int i;
  int oldMode = 0;									// Local variable for old player mode
  int file_piece = 0;

  VS1063sendCMD(SCI_DECODE_TIME, 0);                // Reset DECODE_TIME

  // Main playback loop
  openFile(context.cur_file);
  if(context.cur_file == NULL) return;				// File couldn't be open
  //
  while ((iFResult = f_read(&g_sFileObject, playBuf, sizeof(playBuf) - 1, (UINT *)&bytesInBuffer))> 0 && context.play_state != stop)
  {
	if(iFResult != FR_OK)					// Opening file was not successfull
	{
		context.error.SD_error = "Couldn't read file";
		return;
	}
    int *bufP = playBuf;
    while (bytesInBuffer && context.play_state != stop)
    {
      switch (context.play_state)
      {
      	  case play:
      	  {
      		file_piece = ((SDI_MAX_TRANSFER_SIZE < bytesInBuffer) ? SDI_MAX_TRANSFER_SIZE : bytesInBuffer);
      		VS1063sendData(*bufP,file_piece);
      		bufP += file_piece;
      		bytesInBuffer -= file_piece;
      		file_pos += file_piece;
      		endFillByte = VS1063readData(PAR_END_FILL_BYTE);
      	  }
      	  	  break;
      	  case stop:
      	  {
      		  oldMode = VS1063readData(SCI_MODE);			// Get current player setup
      		  VS1063sendCMD(SCI_MODE, oldMode | SM_CANCEL);	// Cancel playback but keep old setup
      		  context.file_pos = file_pos;						// Save file position
      	  }
      	  	  break;
      	  case pause:
      	  {
      		  playerPause = VS1063readData(PAR_PLAY_MODE) ^ PAR_PLAY_MODE_PAUSE;	// Get current play mode and toggle pause bit
      		  VS1063sendParam(PAR_PLAY_MODE,PAR_PLAY_MODE_PAUSE);					// Send new play mode with toggeld pause bit
      	  }
      	  	  break;
      	  default:
      		  break;
      }

      VS1063sendCMD(SCI_VOL,context.cur_volume*0x0101);			// Set current volume
    }
  }
  // Earlier we collected endFillByte. Now, just in case the file was broken, or if a cancel playback command has been given, write lots of endFillBytes.
  memset(playBuf, endFillByte, sizeof(playBuf));
  for (i=0; i<SDI_END_FILL_BYTES; i+=SDI_MAX_TRANSFER_SIZE) VS1063sendData(*playBuf, SDI_MAX_TRANSFER_SIZE);


  /* If the file actually ended, and playback cancellation was not
     done earlier, do it now. */
  if (context.play_state == play)
  {
    oldMode = VS1063readData(SCI_MODE);
    VS1063sendCMD(SCI_MODE, oldMode | SM_CANCEL);
    watchDog(true);									// Activate watchdog for loop
    while (VS1063readData(SCI_MODE) & SM_CANCEL) VS1063sendCMD(*playBuf, 2);
    watchDog(false);								// Deactivate watchdog if loop had no error
  }

}

void VS1063setEQ(int band)
{

}
