/*
  spi_sd_ffs.c - Reads from a microSD cards fat file system
                by Micromint Support <support@micromint.com>  Jun-2010

  Copyright (c) 2008-2010 Micromint USA LLC  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
  3. Neither the name Micromint nor the names of its contributors may
     be used to endorse or promote products derived from this software
     without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lpc17xx_uart.h"
#include "lpc17xx_libcfg.h"
#include "lpc17xx_pinsel.h"
#include "lpc17xx_gpio.h"
#include "lincoln_board.h"
#include "rtc.h"
#include "diskio.h"
#include "ff.h"
#include "menu.h"
#include "blink_user_led.h"


/*****************************************************************************/
/* Global Variables */
/*****************************************************************************/
char gcCOM1Buffer[72];
char gcCOM1Pointer=0;
volatile UINT Timer = 0; /* Performance timer (1kHz increment) */
//*****************************************************************************
//
// Defines the size of the buffers that hold the path, or temporary
// data from the SD card.  There are two buffers allocated of this size.
// The buffer size must be large enough to hold the longest expected
// full path name, including the file name, and a trailing null character.
//
//*****************************************************************************
#define PATH_BUF_SIZE   80
//*****************************************************************************
//
// The following are data structures used by FatFs.
//
//*****************************************************************************
static FATFS g_sFatFs;
static DIR g_sDirObject;
static FILINFO g_sFileInfo;
static FIL g_sFileObject;
//*****************************************************************************
//
// This buffer holds the full path to the current working directory.
// Initially it is root ("/").
//
//*****************************************************************************
static char g_cCwdBuf[PATH_BUF_SIZE] = "/";

//*****************************************************************************
//
// A temporary data buffer used when manipulating file paths, or reading data
// from the SD card.
//
//*****************************************************************************
static char g_cTmpBuf[PATH_BUF_SIZE];

/************************** PRIVATE DEFINTIONS *************************/
#define SD_DETECT_PORTNUM	0
#define SD_DETECT_PINNUM	16
typedef enum _sd_connect_status
{
	SD_CONNECTED,
	SD_DISCONNECTED
}sd_connect_status;


/************************** PRIVATE TYPES *************************/



/************************** PRIVATE FUNCTIONS *************************/
/* Interrupt service routines */
void UART0_IRQHandler(void);

/*----------------- INTERRUPT SERVICE ROUTINES --------------------------*/
/***********************************************************************
 * @brief		SysTick Interrupt Handler (1ms)
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void SysTick_Handler(void)
{
  static DWORD prescale_disk_io;

  Timer++;

    /* Disk timer process to be called every 10 ms */
    if ( prescale_disk_io++ >=10 )
     {
       prescale_disk_io = 0;
       //disk_timerproc();
     }
}

/***********************************************************************
 * @brief		UART0 interrupt handler sub-routine
 * @param[in]	None
 * @return 		None
 **********************************************************************/
void UART0_IRQHandler(void)
{
  uint8_t ch;
  int8_t iStatus;
  uint32_t i;
  
  while(1)
  {
     /* Receive character */
     i = UART_Receive(COM1_UART, &ch, 1, NONE_BLOCKING);
     if (i != 1)
       break;
     UART_Send(COM1_UART, &ch, 1, NONE_BLOCKING);
     if ((ch > 0x19) && (ch < 0x7F))
     {
       gcCOM1Buffer[gcCOM1Pointer] = ch;
       gcCOM1Pointer++;
     }
     if (ch == 13)
     {
       iStatus = MenuProcessor(gcCOM1Buffer);
       if(iStatus == BAD_MENU_ENTRY)
       {
          UART_Send(COM1_UART, "Bad command!\n\r", 14, BLOCKING);
       }
       /* Handle too many arguments. */
       else if(iStatus == TOO_MANY_ARGS_MENU)
       {
            UART_Send(COM1_UART, "Too many arguments for command processor!\n\r", 43, BLOCKING);
       }
       /* Execute the command and display error code if one is returned */
       else if(iStatus != 0)
       {
            UART_Send(COM1_UART, "iStatus !=0\n\r", 13, BLOCKING);
       }
       UART_Send(COM1_UART, ">", 1, BLOCKING);
       /* Clear COM1's buffer */
       while (gcCOM1Pointer)
       {
           gcCOM1Buffer[gcCOM1Pointer--]=0;
       }
     }
  }
}
/*------------------ SUPPORTING FUNCTIONS ---------------------------*/
/**********************************************************************
* User Provided RTC Function for FatFs module
*----------------------------------------------------------------------
* This is a real time clock service to be called from
* FatFs module. Any valid time must be returned even if
* the system does not support an RTC.
* This function is not required in read-only cfg.
 **********************************************************************/
DWORD get_fattime()
{
  RTCTime rtc;

  /* Get local time */
  rtc_getTime(&rtc);

  /* Pack date and time into a DWORD variable */
  return ((DWORD) (rtc.RTC_Year - 1980) << 25) | ((DWORD) rtc.RTC_Mon << 21)
                  | ((DWORD) rtc.RTC_Mday << 16) | ((DWORD) rtc.RTC_Hour << 11)
                  | ((DWORD) rtc.RTC_Min << 5) | ((DWORD) rtc.RTC_Sec >> 1);
}
/*********************************************************************
 * @brief		check if SD card is inserted or not
 * @param[in]	none
 * @return 		SD_CONNECTED or SD_DISCONNECTED
 **********************************************************************/
sd_connect_status SD_GetCardConnectStatus(void)
{
        uint32_t csgpio;
	sd_connect_status ret=SD_DISCONNECTED;

	if((GPIO_ReadValue(SD_DETECT_PORTNUM)&(1<<SD_DETECT_PINNUM)))
		ret = SD_CONNECTED;

        csgpio = ((GPIO_ReadValue(SD_DETECT_PORTNUM)&(1<<SD_DETECT_PINNUM)));

	return ret;
}

/*****************************************************************************/
/* Implements the ls entry */
/*****************************************************************************/
int En_ls(int argcounter, char *argument[])
{
    unsigned long ulTotalSize;
    unsigned long ulFileCount;
    unsigned long ulDirCount;
    FRESULT fresult;
    FATFS *pFatFs;
    char msg[72];
    int iNumbChar = 0;
    /* Open the current directory for access. */
    fresult = f_opendir(&g_sDirObject, g_cCwdBuf);
    /* Check for error and return if there is a problem. */
    if(fresult != FR_OK)
    {
        return(fresult);
    }

    ulTotalSize = 0;
    ulFileCount = 0;
    ulDirCount = 0;
    /* Give an extra blank line before the listing. */
    UART_Send(COM1_UART, "\n\r", 2, BLOCKING);
    /* Enter loop to enumerate through all directory entries. */
    for(;;)
    {
        /* Read an entry from the directory. */
        fresult = f_readdir(&g_sDirObject, &g_sFileInfo);

        /* Check for error and return if there is a problem. */
        if(fresult != FR_OK)
        {
            return(fresult);
        }
        /* If the file name is blank, then this is the end of the listing. */
        if(!g_sFileInfo.fname[0])
        {
            break;
        }
        /* If the attribue is directory, then increment the directory count. */
        if(g_sFileInfo.fattrib & AM_DIR)
        {
            ulDirCount++;
        }
        /* Otherwise, it is a file.  Increment the file count, and
           add in the file size to the total. */
        else
        {
            ulFileCount++;
            ulTotalSize += g_sFileInfo.fsize;
        }
        /* Print the entry information on a single line with formatting
           to show the attributes, date, time, size, and name. */
        iNumbChar = sprintf(msg,"%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s\n\r",
                                (g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
                                (g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
                                (g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
                                (g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
                                (g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
                                (g_sFileInfo.fdate >> 9) + 1980,
                                (g_sFileInfo.fdate >> 5) & 15,
                                 g_sFileInfo.fdate & 31,
                                (g_sFileInfo.ftime >> 11),
                                (g_sFileInfo.ftime >> 5) & 63,
                                 g_sFileInfo.fsize,
                                 g_sFileInfo.fname);
        UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    }
    /* Print summary lines showing the file, dir, and size totals. */
    iNumbChar = sprintf(msg,"\n\r%4u File(s),%10u bytes total\n\r%4u Dir(s)",
                             ulFileCount, ulTotalSize, ulDirCount);
    UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    /* Get the free space. */
    fresult = f_getfree("/", &ulTotalSize, &pFatFs);
    /* Check for error and return if there is a problem. */
    if(fresult != FR_OK)
    {
        return(fresult);
    }
    /* Display the amount of free space that was calculated. */
    iNumbChar = sprintf(msg,", %10uK bytes free\n\r", ulTotalSize * pFatFs->csize / 2);
    UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    /* Return success. */
    return(0);
}
/*****************************************************************************/
/* Implements the cd entry */
/*****************************************************************************/
int En_cd(int argcounter, char *argument[])
{
    unsigned int uIdx;
    FRESULT fresult;
    char msg[72];
    int iNumbChar = 0;

    /* Copy the current working path into a temporary buffer so
       it can be manipulated. */
    strcpy(g_cTmpBuf, g_cCwdBuf);

    /* If the first character is /, then this is a fully specified
       path, and it should just be used as-is. */
    if(argument[1][0] == '/')
    {
        /* Make sure the new path is not bigger than the cwd buffer. */
        if(strlen(argument[1]) + 1 > sizeof(g_cCwdBuf))
        {
            UART_Send(COM1_UART, "Resulting path name is too long\n\r", 33, BLOCKING);
            return(0);
        }

        /* If the new path name (in argument[1])  is not too long, then
           copy it into the temporary buffer so it can be checked. */
        else
        {
            strncpy(g_cTmpBuf, argument[1], sizeof(g_cTmpBuf));
        }
    }

    /* If the argument is .. then attempt to remove the lowest level on the CWD. */
    else if(!strcmp(argument[1], ".."))
    {
        /* Get the index to the last character in the current path. */
        uIdx = strlen(g_cTmpBuf) - 1;

        /* Back up from the end of the path name until a separator (/)
           is found, or until we bump up to the start of the path. */
        while((g_cTmpBuf[uIdx] != '/') && (uIdx > 1))
        {
            /* Back up one character. */
            uIdx--;
        }

        /* Now we are either at the lowest level separator in the
           current path, or at the beginning of the string (root).
           So set the new end of string here, effectively removing
           that last part of the path. */

        g_cTmpBuf[uIdx] = 0;
    }

    /* Otherwise this is just a normal path name from the current
       directory, and it needs to be appended to the current path. */
    else
    {
        /* Test to make sure that when the new additional path is
           added on to the current path, there is room in the buffer
           for the full new path.  It needs to include a new separator,
           and a trailing null character. */

        if(strlen(g_cTmpBuf) + strlen(argument[1]) + 1 + 1 > sizeof(g_cCwdBuf))
        {
            UART_Send(COM1_UART, "Resulting path name is too long\n\r", 33, BLOCKING);
            return(0);
        }
        /*  The new path is okay, so add the separator and then append
            the new directory to the path. */
        else
        {
            /* If not already at the root level, then append a / */
            if(strcmp(g_cTmpBuf, "/"))
            {
                strcat(g_cTmpBuf, "/");
            }

            /* Append the new directory to the path. */
            strcat(g_cTmpBuf, argument[1]);
        }
    }

    /* At this point, a candidate new directory path is in chTmpBuf.
      Try to open it to make sure it is valid. */

    fresult = f_opendir(&g_sDirObject, g_cTmpBuf);


    /* If it cant be opened, then it is a bad path.  Inform user and return. */
    if(fresult != FR_OK)
    {
        iNumbChar = sprintf(msg,"cd: %s\n\r", g_cTmpBuf);
        UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
        return(fresult);
    }

    /* Otherwise, it is a valid new path, so copy it into the CWD. */
    else
    {
        strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
    }

    /* Return success. */
    return(0);
}
/*****************************************************************************/
/* Implements the wd entry */
/*****************************************************************************/
int En_wd(int argcounter, char *argument[])
{
    char msg[72];
    int iNumbChar = 0;
    /* Show the current working directory. */
    iNumbChar = sprintf(msg,"\n\r\n\r%s\n\r", g_cCwdBuf);
    UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    /* Return success. */
    return(0);
}
/*****************************************************************************/
/* Implements the sf entry */
/*****************************************************************************/
int En_sf(int argcounter, char *argument[])
{
    FRESULT fresult;
    unsigned int uiBytesRead;
    int iNumbChar = 0;
    /* First, check to make sure that the current path (CWD), plus
       the file name, plus a separator and trailing null, will all
       fit in the temporary buffer that will be used to hold the
       file name.  The file name must be fully specified, with path,
       to FatFs. */
    if(strlen(g_cCwdBuf) + strlen(argument[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
        UART_Send(COM1_UART, "Resulting path name is too long\n\r", 33, BLOCKING);
        return(0);
    }
    /* Copy the current path to the temporary buffer so it can be manipulated.*/
    strcpy(g_cTmpBuf, g_cCwdBuf);
    /* If not already at the root level, then append a separator. */
    if(strcmp("/", g_cCwdBuf))
    {
        strcat(g_cTmpBuf, "/");
    }
    /* Now finally, append the file name to result in a fully specified file. */
    strcat(g_cTmpBuf, argument[1]);
    /* Open the file for reading. */
    fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);
    /* If there was some problem opening the file, then return an error. */
    if(fresult != FR_OK)
    {
        return(fresult);
    }
    UART_Send(COM1_UART, "\n\r", 2, BLOCKING);
    /* Enter a loop to repeatedly read data from the file and display it,
       until the end of the file is reached. */
    do
    {
        /* Read a block of data from the file.  Read as much as can fit
           in the temporary buffer, including a space for the trailing null. */
        fresult = f_read(&g_sFileObject, g_cTmpBuf, sizeof(g_cTmpBuf) - 1,
                         &uiBytesRead);
        /* If there was an error reading, then print a newline and
           return the error to the user. */
        if(fresult != FR_OK)
        {
            UART_Send(COM1_UART, "\n\r", 2, BLOCKING);
            return(fresult);
        }
        g_cTmpBuf[uiBytesRead] = 0;
        /* Print the last chunk of the file that was received. */
        iNumbChar = sizeof(g_cTmpBuf);
        UART_Send(COM1_UART, (uint8_t *)g_cTmpBuf, iNumbChar, BLOCKING);
    /* Continue reading until less than the full number of bytes are
       read.  That means the end of the buffer was reached. */
    }
    while(uiBytesRead == sizeof(g_cTmpBuf) - 1);
    UART_Send(COM1_UART, "\n\r", 2, BLOCKING);
    /* Return success. */
    return(0);
}
/*****************************************************************************/
/* Implements the menu entry */
/*****************************************************************************/
int En_menu(int argcounter, char *argument[])
{
    tMenuEntry *pEntry;
    char msg[72];
    int iNumbChar = 0;
    iNumbChar = sprintf(msg,"\n\rLincoln 60 System Clock = %d\n\r", SystemCoreClock);
    UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    /* Display header */
    iNumbChar = sprintf(msg,"Available Commands\n\r");
    UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
    /* Point to the beginning of the menu. */
    pEntry = &g_sMenu[0];
    /* Read all of the entries on the menu until the a NULL is reached */
    while(pEntry->pcMenu)
    {
        /* Display the menu entry and a breif description. */
        iNumbChar = sprintf(msg,"%s%s\n\r", pEntry->pcMenu, pEntry->pcMenuDescription);
        UART_Send(COM1_UART, (uint8_t *)msg, iNumbChar, BLOCKING);
        /* Advance to the next entry in the menu. */
        pEntry++;
    }
    /* Return success. */
    return(0);
}

/*****************************************************************************/
tMenuEntry g_sMenu[] =
{
    { "m",     "     : Display Menu",                          En_menu  },
    { "?",     "     : alias for menu",                        En_menu  },
    { "ls",    "    : Display list of files",                 En_ls    },
    { "cd",    "    : Change directory",                      En_cd    },
    { "wd",    "    : Show current working directory",        En_wd    },
    { "sf",    "    : Show contents of text file",            En_sf    },
    { 0, 0, 0 }
};
/*-------------------------MAIN FUNCTION------------------------------*/
/*********************************************************************//**
 * @brief		c_entry: Main UART program body
 * @param[in]	None
 * @return 		int
 **********************************************************************/
int c_entry(void)
{
    FRESULT fresult;
    // UART Configuration structure variable
    UART_CFG_Type UARTConfigStruct;
    // UART FIFO configuration Struct variable
    UART_FIFO_CFG_Type UARTFIFOConfigStruct;
    // Pin configuration for UART0
    PINSEL_CFG_Type PinCfg;

    /* Initialize UART0 pin connect	 */
    PinCfg.Funcnum = 1;
    PinCfg.OpenDrain = 0;
    PinCfg.Pinmode = 0;
    PinCfg.Pinnum = 2;
    PinCfg.Portnum = 0;
    PINSEL_ConfigPin(&PinCfg);
    PinCfg.Pinnum = 3;
    PINSEL_ConfigPin(&PinCfg);

    /* Initialize UART Configuration parameter structure to default state: */
    UART_ConfigStructInit(&UARTConfigStruct);
    //Set the baudrate to 115200
    UARTConfigStruct.Baud_rate = 115200;

    // Initialize UART0 peripheral with given to corresponding parameter
    UART_Init((LPC_UART_TypeDef *)LPC_UART0, &UARTConfigStruct);

    /* Initialize FIFOConfigStruct to default state:
    * 				- FIFO_DMAMode = DISABLE
    * 				- FIFO_Level = UART_FIFO_TRGLEV0
    * 				- FIFO_ResetRxBuf = ENABLE
    * 				- FIFO_ResetTxBuf = ENABLE
    * 				- FIFO_State = ENABLE
    */
    UART_FIFOConfigStructInit(&UARTFIFOConfigStruct);

    // Initialize FIFO for UART0 peripheral
    UART_FIFOConfig((LPC_UART_TypeDef *)LPC_UART0, &UARTFIFOConfigStruct);

    // Enable UART Transmit
    UART_TxCmd((LPC_UART_TypeDef *)LPC_UART0, ENABLE);


    /* Enable UART line status interrupt */
    UART_IntConfig((LPC_UART_TypeDef *)LPC_UART0, UART_INTCFG_RLS, ENABLE);

    /* preemption = 1, sub-priority = 1 */
    NVIC_SetPriority(UART0_IRQn, ((0x01<<3)|0x01));
	/* Enable Interrupt for UART0 channel */
    NVIC_EnableIRQ(UART0_IRQn);
    /* Intialize the green LED to blink using a PWM*/
    Init_Green_LED_PWM();
    /* Blink the Green LED 4 times a second */
    Blink_Green_LED_PWM(4);

    /* Initialize SysTick timer */
    SysTick_Config(SystemCoreClock / 1000 - 1); /* Generate interrupt 1 ms   */
    /* Initialize SD card detection pin */
    PinCfg.Portnum = 0;
    PinCfg.Pinnum = 16;
    PinCfg.Pinmode = PINSEL_PINMODE_PULLDOWN;
    PinCfg.Funcnum = 0;//GPIO function
    PinCfg.OpenDrain = 0;
    PINSEL_ConfigPin(&PinCfg);
    GPIO_SetDir(SD_DETECT_PORTNUM, (1<<SD_DETECT_PINNUM), 0);//input
    /* check for SD card is inserted */
    if(SD_GetCardConnectStatus()==SD_DISCONNECTED)
    {
       UART_Send(COM1_UART, "\n\rPlease plug-in SD card!\n\r", 27, BLOCKING);
       while(SD_GetCardConnectStatus()==SD_DISCONNECTED) ;
    }
    UART_Send(COM1_UART, "...Connected!\n\r", 15, BLOCKING);
    MenuProcessor("m");
    UART_Send(COM1_UART, ">", 1, BLOCKING);
    /* Enable UART Rx interrupt */
    UART_IntConfig((LPC_UART_TypeDef *)LPC_UART0, UART_INTCFG_RBR, ENABLE);
    //
    // Mount the file system, using logical disk 0.
    //
    fresult = f_mount(0, &g_sFatFs);
    if(fresult != FR_OK)
    {
        UART_Send(COM1_UART, "f_mount error:\n\r", 16, BLOCKING);
        return(1);
    }
    while(1)
    {

    }
    return 1;
}
/*****************************************************************************/

/* With ARM and GHS toolsets, the entry point is main() - this will
   allow the linker to generate wrapper code to setup stacks, allocate
   heap area, and initialize and copy code and data segments. For GNU
   toolsets, the entry point is through __start() in the crt0_gnu.asm
   file, and that startup code will setup stacks and data */
int main(void)
{
    return c_entry();
}

#ifdef  DEBUG
/*******************************************************************************
* @brief		Reports the name of the source file and the source line number
* 				where the CHECK_PARAM error has occurred.
* @param[in]	file Pointer to the source file name
* @param[in]    line assert_param error line source number
* @return		None
*******************************************************************************/
void check_failed(uint8_t *file, uint32_t line)
{
	/* User can add his own implementation to report the file name and line number,
	 ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

	/* Infinite loop */
	while(1);
}
#endif

/*
 * @}
 */
