/******************************************************************************
 * SSD1926 hardware JPEG decoder and SD card interface demo
 * The demo displays all JPEG files in the SD card root location.
 * The card must be inserted before the demo starts.
 *
 * The demo will also play video files formated as RGB565 for each frame .
 * Use ffmpeg to do the video conversion, an example ffmpeg command line is:
 * ffmpeg -i video.avi -s 320x240 -pix_fmt rgb565 video.rgb
 *
 * All display orientations are supported. Resolution of the images must be less than 2560x1920
 * (SSD1926 maximum hardware scale down factor is 8).
 *
 * FUNCTIONS IMPLEMENTED:
 *	void SSD1926SetRGB(void) - switch display to RGB,
 *  void SSD1926SetYUV(void) - switch display to YUV (most JPEG files have this format),
 *  BYTE JPEGPutImage(char* filename) - display JPEG file in the center.
 *
 * MUST BE IN PROJECT:
 *  FSIO.c - file system,
 *  DisplayDriver.c - graphics library,
 *  Primitive.c - graphics library,
 *  SSD1926_SDCard.c - SD card access functions for the file system,
 *  SSD1926_JPEG.c - JPEG decoder functions.
 ******************************************************************************
 * FileName:        MainDemo.c
 * Dependencies:    see included files below
 * Processor:       PIC24F, PIC24H, dsPIC, PIC32
 * Compiler:        C30 v2.01/C32 v0.00.18
 * Company:         Microchip Technology, Inc.
 * Software License Agreement
 *
 * Copyright (c) 2011 Microchip Technology Inc.  All rights reserved.
 * Microchip licenses to you the right to use, modify, copy and distribute
 * Software only when embedded on a Microchip microcontroller or digital
 * signal controller, which is integrated into your product or third party
 * product (pursuant to the sublicense terms in the accompanying license
 * agreement).  
 *
 * You should refer to the license agreement accompanying this Software
 * for additional information regarding your rights and obligations.
 *
 * SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
 * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
 * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR
 * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION,
 * BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT
 * DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL,
 * INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
 * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY
 * CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
 * OR OTHER SIMILAR COSTS.
 *******************************************************************************/
/************************************************************
 * Includes
 ************************************************************/

/*
************************************************************
* See Readme.txt for modifications
************************************************************
*/

#include "MainDemo.h"
#include "Graphics/SSD1926.h"

/************************************************************
 * Configuration Bits
 ************************************************************/
#if defined(__PIC32MX__)
	#if defined (__32MX360F512L__) || defined (__32MX460F512L__) || defined (__32MX795F512L__)
	    #pragma config FPLLODIV = DIV_1, FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FWDTEN = OFF, FCKSM = CSECME, FPBDIV = DIV_1
	    #pragma config OSCIOFNC = ON, POSCMOD = XT, FSOSCEN = ON, FNOSC = PRIPLL
	    #pragma config CP = OFF, BWP = OFF, PWP = OFF
	#elif defined (__32MX150F128B__)
	    #pragma config FPLLODIV = DIV_2, FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FWDTEN = OFF, FCKSM = CSECME, FPBDIV = DIV_1
	    #pragma config OSCIOFNC = OFF, POSCMOD = OFF, FSOSCEN = OFF, FNOSC = FRCPLL, ICESEL = ICS_PGx1
	    #pragma config CP = OFF, BWP = OFF, PWP = OFF, DEBUG = ON, JTAGEN = OFF
	#endif
#endif

/************************************************************
 * Defines
 ************************************************************/
#define START_ADD   0ul
#define FRAME_SIZE (320ul * 240ul * 2ul)
//#define FRAME_SIZE (128ul * 128ul * 2ul)

/************************************************************
 * Enumeration Structure
 ************************************************************/
typedef enum
{
    DEMO_FILE_TYPE_JPEG,
    DEMO_FILE_TYPE_RGB,
    DEMO_FILE_TYPE_OTHER
}DEMO_FILE_TYPE;

/************************************************************
 * Externs: Function Prototypes
 ************************************************************/
extern BYTE FILEget_next_cluster(FSFILE *fo, DWORD n);
extern DWORD Cluster2Sector(DISK * disk, DWORD cluster);
extern BYTE SDSectorDMARead(DWORD sector_addr, DWORD dma_addr, UINT16 num_blk);

/************************************************************
 * Function Prototypes
 ************************************************************/
DEMO_FILE_TYPE GetFileType(char *fileName);
void PlayRGB(const char * fileName);

SHORT KeyGetKey();

/************************************************************
 * Variables
 ************************************************************/
DEMO_FILE_TYPE currentFileType;

/************************************************************
 * int main (void)
 ************************************************************/
int main (void)
{
    SearchRec nextFile;

    #if defined(__PIC32MX__)

	    INTEnableSystemMultiVectoredInt();
	    SYSTEMConfigPerformance(GetSystemClock());	

		//#if defined (__32MX360F512L__)
		#if defined (PIC32_SK) && (defined (__32MX360F512L__) || defined (__32MX460F512L__) || defined (__32MX795F512L__))
			AD1PCFG = 0xffff;		//all digital operation
		#elif defined (__32MX150F128B__)
			ANSELA = 0x00;
			ANSELB = 0x00;
		#endif 

        //SYSTEMConfig(GetSystemClock(), SYS_CFG_ALL);
		#if defined (GFX_SSD1928_CAMERA_R2_4_B)
			HardwareButtonInit(); //initialize tact switch / wheel onboard
			OutputHWPowerCtrlOn();
			BeepInit();
			Beep();
			DelayMs(100);
			Beep();
		#endif

	#endif

    //InitGraph();                // Graphics
    //FSInit();                   // File system

	SSD1926HardwareReset();

	SSD1355ResetDevice();

	SetColor(BRIGHTRED);
	SSD1926SubClearDevice();

	while (!FSInit());                   // File system

	asm("nop");
	SSD1926SubLCDSendCommand(0x2C);			//memory write, important for SSD1355 as auto refresh mode only push data out
	SSD1926SubLCDSetWriteMode(AUTO_REFRESH_MODE);
	asm("nop");

	SSD1926SetPanel(SUB_PANEL);

	SSD1926SetYUV();    //  
						// set DISP_HOR_RESOLUTION 128, set DISP_VER_RESOLUTION 128 to display on OLED properly!

	if(-1 != FindFirst("Weather.jpg", ATTR_ARCHIVE, &nextFile))
	{
		if(GetFileType(nextFile.filename) == DEMO_FILE_TYPE_JPEG)  
		{
			JPEGPutImage(nextFile.filename);
		}
	}

	InitGraph();				//Graphics init for the main panel in 320x240 and switch to main panel with this function
	SSD1926SetPanel(MAIN_PANEL);//this is necessary after InitGraph(), otherwise, JPEGResize(SSD_JPEG_DECODE *decode)
								//won't work for this.
	//debug JPEG ENCODE
	SSD1928CamInit();			//low level initialization
	SSD1928CamPreview(0, 1);	//turn on preview
	ssd192x_jpeg_init();
	Set_JPEG_Encode_Registers(_param.previewCropHSize, _param.previewCropVSize);

	UINT32 file_size = Start_JPEG_Encode();
	
	if(file_size)
		Write_JPEG_data_to_SD(file_size, 16);
	else
		Nop();//assert write fail or JPEG encode fail.

	while(KeyGetKey()!=HW_WHEEL_DOWN);	//wait infinitely for key "wheel scroll down" 
	Beep();
	SSD1928CamPreview(0, 0);			//turn off preview, show the still picture
	while(KeyGetKey()!=HW_WHEEL_DOWN);	//wait infinitely for key "wheel scroll down"
	Beep();

    currentFileType = DEMO_FILE_TYPE_RGB;

    while(1)
    {        
	   if(-1 != FindFirst("*.*", ATTR_ARCHIVE, &nextFile))
	   {
		            do
		            {
		                if(GetFileType(nextFile.filename) == DEMO_FILE_TYPE_JPEG)   
		                {
		                    if(currentFileType != DEMO_FILE_TYPE_JPEG)
		                    {
		                        // Set YUV mode to display JPEG
		                        SetColor(BLACK);    // Black in RGB is dark green in YUV
		                        ClearDevice();
		                        SSD1926SetYUV();    // Switching shows a little green flicker
		                        currentFileType = DEMO_FILE_TYPE_JPEG;
		                    }
		                    
		                    JPEGPutImage(nextFile.filename); 
		                    //DelayMs(2000);
							while(KeyGetKey()!=HW_WHEEL_DOWN);	//wait infinitely for key "wheel scroll down"
							Beep();		
		                }
		                else if(GetFileType(nextFile.filename) == DEMO_FILE_TYPE_RGB)   
		                {
		                    if(currentFileType != DEMO_FILE_TYPE_RGB)
		                    {
		                        // Set YUV mode to display JPEG
		                        SetColor(BLACK);    // Black in RGB is dark green in YUV
		                        ClearDevice();
		                        SSD1926SetRGB();    // Switching shows a little green flicker
		                        currentFileType = DEMO_FILE_TYPE_RGB;
		                    }
		                    
		                    PlayRGB(nextFile.filename);
		                    
		                }
		                else	//it can be BITMAP or other file type in SD card, do nothing
		                {
		                }
		            } while(-1 != FindNext(&nextFile));
		}
    }

    return (-1);
}
/************************************************************
 * DEMO_FILE_TYPE GetFileType(char *fileName)
 ************************************************************/
DEMO_FILE_TYPE GetFileType(char *fileName)
{
   
    while(*fileName != '.')
        fileName++;

    fileName++;

    // check to see if it is a jpeg
    if((fileName[0] == 'J') && (fileName[1] == 'P') && (fileName[2] == 'G'))
        return DEMO_FILE_TYPE_JPEG;

    if((fileName[0] == 'j') && (fileName[1] == 'p') && (fileName[2] == 'g'))
        return DEMO_FILE_TYPE_JPEG;

    // check to see if it is a rgb movie
    if((fileName[0] == 'R') && (fileName[1] == 'G') && (fileName[2] == 'B'))
        return DEMO_FILE_TYPE_RGB;

    if((fileName[0] == 'r') && (fileName[1] == 'g') && (fileName[2] == 'b'))
        return DEMO_FILE_TYPE_RGB;

    return DEMO_FILE_TYPE_OTHER;
}


/************************************************************
 * WORD StreamRGBFrame(FSFILE *fileStream, WORD numSectors)
 * This is a hack of the MDD file system to be able to use 
 * the SSD1926 SD Card DMA feature.
 ************************************************************/
WORD StreamRGBFrame(FSFILE *fileStream, WORD numSectors)
{
    DISK 		*dsk;
    DWORD       sec_sel;
    WORD       sectorsToRead;
    DWORD       currentCluster;
    DWORD       prevousCluster;
    static DWORD add = START_ADD;

    dsk    = (DISK *)fileStream->dsk;

    sec_sel = Cluster2Sector(dsk, fileStream->ccls);
    sec_sel += (WORD)fileStream->sec;      // add the sector number to it
    
    currentCluster = fileStream->ccls;
    prevousCluster = currentCluster;

    // This will be the minimum sectors that are available by the card to be read
    sectorsToRead = (WORD)dsk->SecPerClus - (WORD)fileStream->sec;

    // get as many sectors from clusters that are contiguous
    while(sectorsToRead < numSectors)
    {
        if( FILEget_next_cluster( fileStream, 1) != CE_GOOD )
            return 0xFFFF;
        
        if((prevousCluster + 1) != fileStream->ccls)
        {
            fileStream->ccls = prevousCluster;
            break;
        }

        prevousCluster++;
        sectorsToRead += dsk->SecPerClus;
    }
    
    // make sure that we are not over the bounds
    if(sectorsToRead > numSectors)
        sectorsToRead = numSectors;

    // do a DMA write of the information from the SD card to the display buffer
    if (!SDSectorDMARead(sec_sel,  add, sectorsToRead))
    {
        fileStream->ccls = currentCluster;
        return 0;
    }
    else
    {
        // update the address
        add += (dsk->sectorSize * (DWORD)sectorsToRead);
        
        if (add >= (FRAME_SIZE + START_ADD)) 
        {
            add = START_ADD;
        }
        
        // update the pointers
        fileStream->seek += (dsk->sectorSize * sectorsToRead);
        
        if (fileStream->seek > fileStream->size)
        {
            fileStream->seek = fileStream->size;
            return 0xFFFF;
        }
    }

    // get the current sector within the current cluster
    currentCluster = fileStream->sec + sectorsToRead;
    while(currentCluster > dsk->SecPerClus)
        currentCluster -= dsk->SecPerClus;

    fileStream->sec = currentCluster;
    
    // get a new cluster if necessary
    if( fileStream->sec == dsk->SecPerClus )
    {
        fileStream->sec = 0;
        if( FILEget_next_cluster( fileStream, 1) != CE_GOOD )
            return 0xFFFF;
    }

    return sectorsToRead;

}
/************************************************************
 * void PlayRGB(const char * fileName)
 ************************************************************/
void PlayRGB(const char * fileName)
{

	DWORD total_frames;
    WORD sectorsToWrite;
	FSFILE *pFile = NULL;
    int i;
    BYTE reg;

    reg = GetReg(REG_SPECIAL_EFFECTS);

    SetReg(REG_SPECIAL_EFFECTS, reg & 0xBF);	//still working with this removed. Why it is here?

    pFile = FSfopen(fileName, "rb");

    total_frames = (pFile->size / FRAME_SIZE);

	SetColor(BLACK);
	ClearDevice();
	
	DisplayEnable();	// Aseert chip select to SSD1926

    for (i = 0; i < total_frames; i++) 
    {
        sectorsToWrite = FRAME_SIZE / pFile->dsk->sectorSize;

        while(sectorsToWrite)
        {
            WORD result = StreamRGBFrame(pFile, sectorsToWrite);

            if(result == 0xFFFF)
                break;

            sectorsToWrite -= result;
        }
    }

    // Clear screen back to black before changing the hardware SFR byte swap back
	SetColor(BLACK);
	ClearDevice();
	
    SetReg(REG_SPECIAL_EFFECTS, reg);

    DisplayDisable();       // De-assert chip select to SSD1926	
    
    FSfclose(pFile);
}

/*********************************************************************
* Function:  	KeyGetKey()
*
* Overview: 	A simple keypad function to return the key pressed
*
* PreCondition: Definitions on HW_BUTTON_PRESS, _RELEASE, etc 
*				in hardware profile
*
* Input: 		none
*
* Return: 		-1 if no valid keypress
*				enum HW_PWR = 0, HW_WHEEL_TAP, HW_WHEEL_UP, 
*				HW_WHEEL_DOWN defined in hardware profile
*
* Remarks:		No OS assumed, this function halts the main for 10ms
*				as key debounce. No key auto-repeat is allowed.
*				Need to release the key and press again for another
*				valid key press.
********************************************************************/
SHORT KeyGetKey()
{
	static BYTE _state = HW_BUTTON_RELEASE;

	if(	GetHWPower() == HW_PWR_PRESS || \
		GetHWWheelTap() == HW_WHEEL_TAP_PRESS
		#ifndef __DEBUG	//for PIC32MX150F128B because wheel-up and wheel-down multiplex with ICSP's DAT&CLK lines
		|| GetHWWheelUp() == HW_WHEEL_UP_PRESS|| \
		GetHWWheelDown() == HW_WHEEL_DOWN_PRESS
		#endif
		)
	{
		if(_state == HW_BUTTON_RELEASE)
		{
			DelayMs(10);	//debounce
	
			if(GetHWPower() == HW_PWR_PRESS)
				{_state = HW_BUTTON_PRESS; return (HW_PWR);}
			else if (GetHWWheelTap() == HW_WHEEL_TAP_PRESS)
				{_state = HW_BUTTON_PRESS; return (HW_WHEEL_TAP);}
		#ifndef __DEBUG
			else if (GetHWWheelUp() == HW_WHEEL_UP_PRESS)
				{_state = HW_BUTTON_PRESS; return (HW_WHEEL_UP);}
			else if (GetHWWheelDown() == HW_WHEEL_DOWN_PRESS)
				{_state = HW_BUTTON_PRESS; return (HW_WHEEL_DOWN);}
		#endif
		}
		else
		{
			return (-1);
		}
	}
	else
	{
		_state = HW_BUTTON_RELEASE;
		return(-1);	//no key press
	}
}
