/* example_c_decode_file - Simple FLAC file decoder using libFLAC
 * Copyright (C) 2007  Josh Coalson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
/*******************************************************************************
* Company: Microsemi Corporation
*
* File: main.c
* File history:
*      Revision: 1.0 Date: Aug 2, 2011
*
* Description:
*
*Porting FLAC (Free Lossless Audio Codec)player on SmartFusion
*
* Author: Gangadhar Yadiki / Sathish Kumar Donthu
*         Sathish.Donthu@microsemi.com
*         Corporate Applications Engineering
*
*******************************************************************************/
/*
 * This example shows how to use libFLAC to decode a FLAC file to a WAVE
 * file.  It only supports 16-bit stereo files.
 *
 * Complete API documentation can be found at:
 *   http://flac.sourceforge.net/api/
 */


#if HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include "FLAC/stream_decoder.h"
#include "FLAC/metadata.h"
/**************************************************************************/
/* Firmware Includes */
/**************************************************************************/
#include "./drivers/mss_uart/mss_uart.h"
#include "./drivers/mss_rtc/mss_rtc.h"
#include "./drivers/mss_timer/mss_timer.h"
#include "./drivers/mss_ace/mss_ace.h"
#include "./drivers/mss_pdma/mss_pdma.h"
#include "./drivers/mss_gpio/mss_gpio.h"
//FAT
#include "../ff.h"
#define fabric_address 0x40050200	//address of player in fabric

/*******************************Decoder call backs*******************************/
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);

/**********************Global variables to hold sample information****************/
volatile FLAC__uint64 total_samples = 0;
volatile unsigned sample_rate = 0;
static unsigned channels = 0;
static unsigned bps = 0;

/*******************************File System variables*****************************/
FATFS fatfs;				/* File system object */
extern FIL file1;		    /* File object */
extern char *ptr;
extern volatile BYTE filecontent;
extern void put_rc (FRESULT rc);
/*********************************************************************************/

volatile BYTE firsttime;		//FLAGS to synchronize the decode and play
int32_t PlayBuffer[4096];		//Play buffer

static void spi_flash_loader(void);

void init_system()
{
	/* Initialize and configure UART0. */
	MSS_UART_init
	(
			&g_mss_uart0,
			MSS_UART_57600_BAUD,
			MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY | MSS_UART_ONE_STOP_BIT
	);
	MSS_GPIO_init();
	MSS_GPIO_config(MSS_GPIO_0, MSS_GPIO_INPUT_MODE);
	MSS_GPIO_config(MSS_GPIO_1 ,MSS_GPIO_OUTPUT_MODE );
	MSS_GPIO_config(MSS_GPIO_2, MSS_GPIO_INPUT_MODE);
	MSS_GPIO_config(MSS_GPIO_3, MSS_GPIO_OUTPUT_MODE );
	MSS_GPIO_config(MSS_GPIO_5, MSS_GPIO_INPUT_MODE  );
	MSS_GPIO_config(MSS_GPIO_4 ,MSS_GPIO_OUTPUT_MODE );
}

void Init_FS()
{
	long p2;
	FATFS *fs;

	/* File System  on SPI Flash Init */
	if (disk_initialize(0))
	{
		iprintf("Disk Initialization Failed: SPI interface Error \n\r");
	}
	else
	{
	   f_mount(0, &fatfs);

       if(f_getfree("", (DWORD*)&p2, &fs))
	   {
           /* Create the File System */
    	   if (f_mkfs(0, 0, 2048))
           {
       	       iprintf("File System Cann't be created \n\r");
           }
	   }

	}
    MSS_RTC_init();
    MSS_RTC_configure( MSS_RTC_NO_COUNTER_RESET );
    MSS_RTC_start();
}


size_t UART_Polled_Rx
(
   mss_uart_instance_t * this_uart,
   uint8_t * rx_buff,
   size_t buff_size
)
{
   size_t rx_size = 0U;
   while( rx_size < buff_size )
   {
      while ( this_uart->hw_reg_bit->LSR_DR != 0U  )
      {
         rx_buff[rx_size] = this_uart->hw_reg->RBR;
         ++rx_size;
      }
   }
   return rx_size;
}

/*==============================================================================
 * main function.
 */
void Handshake()
{
   uint8_t rx_buff[1];

   /* Handshake */
   while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
      ;
   if(rx_buff[0] == 'h')
      MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t * )"a", 1 );

   while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
      ;

   if(rx_buff[0] == 'n')
      MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t * )"d", 1 );

   while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
      ;

   if(rx_buff[0] == 's')
      MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t * )"h", 1 );

   while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
      ;

   if(rx_buff[0] == 'a')
   MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t * )"k", 1 );

   while(!(UART_Polled_Rx ( &g_mss_uart0, rx_buff, 1 )))
      ;

   if(rx_buff[0] == 'e')
   {
      /* EMC or SPI Flash*/
      while( ! UART_Polled_Rx(&g_mss_uart0, rx_buff, 1))
         ;
      if(rx_buff[0] == 's')
      {
         spi_flash_loader();
      }
      else
      {
         MSS_UART_polled_tx( &g_mss_uart0, (const uint8_t * )"Selected Option is wrong, Please enter correct value", sizeof("Selected Option is wrong, Please enter correct value") );
      }

   }
   return 0;
}
/*==============================================================================
 * main function.
 */
static void spi_flash_loader(void)
{
   size_t rx_size;
   uint8_t img_buffer[1024];
   uint32_t  address, oriAddress;
   int32_t img_size,ii, oriSize;
   UINT length;
   UINT length2;
   uint8_t res;

   /* Read the file and burn into external flash */
   while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_size, 4));
   MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t * )&img_size, 4 );
   oriSize = img_size;
   /* Read the file and burn into external flash */
   while( ! UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&address, 4));
   MSS_UART_polled_tx( &g_mss_uart0,(const uint8_t * )&address, 4 );
   oriAddress = address;
   ii = 0;

   while(img_size > 0)
   {
      rx_size =0;
      if (img_size >=1024)
         length = 1024;
      else
         length = img_size;

      do
      {
         rx_size = UART_Polled_Rx(&g_mss_uart0, (uint8_t *)&img_buffer, length);
      }
      while(!rx_size);

      if(rx_size != length)
      {
         return;
      }
      img_size -= length;
      // alligning to 32 bit
      if ((length%4) != 0)
      {
         length += (4-(length%4));
      }
      f_lseek(&file1, file1.fsize);
	  res = f_write(&file1, img_buffer, length, &length2);
      ii += length;
      MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t * )"a",1);
   }
   // Verify the last bytes
   {
      uint8_t img_buffer2[1030];
      ii -= length;

      res = f_close(&file1);
      res = f_open(&file1, ptr, FA_OPEN_EXISTING | FA_READ);
      f_lseek(&file1, 0);
      f_lseek(&file1,ii);
      res = f_read(&file1, img_buffer2, length, &length2);

      for(ii=0; ii<length; ii++)
      {
         if(img_buffer[ii] != img_buffer2[ii])
         {
            MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t * )"z",1);
            return;
         }
      }

      MSS_UART_polled_tx(&g_mss_uart0,(const uint8_t * )"y",1);
      f_close(&file1);
      f_open(&file1, ptr, FA_OPEN_EXISTING | FA_READ);
   }
   return;
}


/*==============================================================================
 * main function.
 */
int main(int argc, char *argv[])
{
	FLAC__bool ok = true;
	FLAC__StreamDecoder *decoder = 0;
	FLAC__StreamDecoderInitStatus init_status;
	BYTE res;
	uint32_t TotalCycles =0;
	int returnvalue =0;

	/* Initialization all necessary hardware components */
    init_system();
    Init_FS();

    iprintf("\r SmartFusion FLAC Decoder Demo\n");

    while(1)
    {
		MSS_TIM1_disable_irq();
		firsttime =1;
		iprintf("\r \nPlease enter any command followed by enter key\r\n fs - FS Status\n \r fl - FS Listing\n \r fo <filename> Open file\n");
		iprintf("\r fn <new name> <old name> - Rename File\n \r fx <src_name> <dest_name> - File Copy\n \r fu <filename> - Remove file\n");
		iprintf("\r fm - Format the SPI Flash\n");
		iprintf("\r t <M D YYYY Hr Min > - To set the RTC time\n");

		returnvalue= fatfs_ops();

		while(returnvalue !=3|| filecontent ==0)
		{
			iprintf("\r \nPlease enter any command followed by enter key\r\n fs - FS Status\n \r fl - FS Listing\n \r fo <filename> Open file\n");
			iprintf("\r fn <new name> <old name> - Rename File\n \r fx <src_name> <dest_name> - File Copy\n \r fu <filename> - Remove file\n");
			iprintf("\r fm - Format the SPI Flash\n");
			iprintf("\r t <M D YYYY Hr Min > - To set the RTC time\n");
			returnvalue=fatfs_ops();
		}

    	ok =true;
    	if((decoder = FLAC__stream_decoder_new()) == NULL)
    	{
    		fprintf(stderr, "ERROR: allocating decoder\n");
    		res = f_close(&file1);
    		return 1;
    	}

    	(void)FLAC__stream_decoder_set_md5_checking(decoder, true);

    	init_status = FLAC__stream_decoder_init_file(decoder, &file1, write_callback, metadata_callback, error_callback, /*client_data=*/0);

    	if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
    	{
    		fprintf(stderr, "ERROR: initializing decoder: %s\n", FLAC__StreamDecoderInitStatusString[init_status]);
    		ok = false;
    	}
#if 0             //for calculating the time taken by the total decoder enable this
    	MSS_TIM2_init(MSS_TIMER_ONE_SHOT_MODE);
    	MSS_TIM2_load_immediate(0xFFFFFFFF);
    	MSS_TIM2_start();
#endif
    	if(ok)
    	{
    		ok = FLAC__stream_decoder_process_until_end_of_stream(decoder);
    		fprintf(stderr, "\rdecoding: %s\n", ok? "succeeded" : "FAILED");
    		fprintf(stderr, "\rstate: %s\n", FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(decoder)]);
    	}
#if 0
    	TotalCycles = 0xFFFFFFFF - MSS_TIM2_get_current_value();
    	MSS_TIM2_stop();
    	fprintf(stderr, "\r TotalCycles : %u,TotalTime :%f \n", TotalCycles, (TotalCycles/(double)(80000000)));
#endif

    	FLAC__stream_decoder_delete(decoder);
    	res = f_close(&file1);

    }
    return 0;

}

FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
{
	uint32_t blocksizetemp;
	uint32_t gpio_inputs=0,freq;
    volatile uint32_t status_pdma = 0;
	(void)decoder;

	if(total_samples == 0) {
		fprintf(stderr, "ERROR: this example only works for FLAC files that have a total_samples count in STREAMINFO\n");
		return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
	}

	blocksizetemp = frame->header.blocksize;
	freq=(40000000/sample_rate);

	if(firsttime)
	{
		PDMA_init();
		PDMA_configure
		(
				PDMA_CHANNEL_1,
				PDMA_MEM_TO_MEM,
				PDMA_LOW_PRIORITY | PDMA_WORD_TRANSFER |PDMA_INC_SRC_FOUR_BYTES |PDMA_INC_DEST_FOUR_BYTES,
				0
		);
		PDMA_start
		(
				PDMA_CHANNEL_1,
				(FLAC__int32)buffer[0],
				(int32_t)&PlayBuffer,
				blocksizetemp
		);
		MSS_GPIO_set_output(MSS_GPIO_1,0);
		MSS_GPIO_set_output(MSS_GPIO_1,1);
		(*((uint32_t volatile *) (fabric_address)) = PlayBuffer);
		(*((uint32_t volatile *) (fabric_address)) = freq);
		firsttime =0;
		return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
	}

	while(!( gpio_inputs&1))
		gpio_inputs = MSS_GPIO_get_inputs();

	MSS_GPIO_set_output(MSS_GPIO_1,0);
	PDMA_start
	(
			PDMA_CHANNEL_1,
			(FLAC__int32)buffer[0],
			(int32_t)&PlayBuffer,
			blocksizetemp
	);

	do
	{
		status_pdma = PDMA_status (PDMA_CHANNEL_1);
	}while (status_pdma == 0);

	MSS_GPIO_set_output(MSS_GPIO_1,1);
	(*((uint32_t volatile *) (fabric_address)) = PlayBuffer);
	(*((uint32_t volatile *) (fabric_address)) = freq);
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;


}

void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
{
	(void)decoder, (void)client_data;

	/* print some stats */
	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
	{
		/* save for later */
		total_samples = metadata->data.stream_info.total_samples;
		sample_rate = metadata->data.stream_info.sample_rate;
		channels = metadata->data.stream_info.channels;
		bps = metadata->data.stream_info.bits_per_sample;
		iprintf("\n\r ----------FLAC file is Playing----------------\n");
		fprintf(stderr, "\rsample rate    : %u Hz\n", sample_rate);
		fprintf(stderr, "\rchannels       : %u\n", channels);
		fprintf(stderr, "\rbits per sample: %u\n", bps);
    	fprintf(stderr, "\rtotal samples  : %llu\n", total_samples);
	}
}

void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
	(void)decoder, (void)client_data;

	fprintf(stderr, "Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
}

