#include "main.h"

// System structers
extern struct info sys_info;
extern struct work context;
extern struct qei ENC;

FRESULT iFResult;						// Result from FF file system

// This function returns a string representation of an error code that was
// returned from a function call to FatFs.  It can be used for printing human
// readable error messages.
char* StringFromFResult(FRESULT iFResult)
{
    uint_fast8_t ui8Idx;

    for(ui8Idx = 0; ui8Idx < NUM_FRESULT_CODES; ui8Idx++)    // Enter a loop to search the error code table for a matching error code.
    {
        if(g_psFResultStrings[ui8Idx].iFResult == iFResult)  // If a match is found, then return the string name of the error code.
        return(g_psFResultStrings[ui8Idx].pcResultStr);
    }
    return("Unbekannter Fehler");
}

// This is the handler for this SysTick interrupt.  FatFs requires a timer tick every 10 ms for internal timing purposes.
void SysTickHandler(void)
{
    disk_timerproc(); // Call the FatFs tick timer.
}

// This function implements the "ls" command.  It opens the current directory
// and enumerates through the contents, and prints a line for each item it
// finds. It shows details such as file attributes, time and date, and the
// file size, along with the name.
void getDirContent()
{
	unsigned int counter = 0;				// Counter to iterrate through dir and file list
    uint32_t ui32TotalSize = 0;
    uint32_t ui32FileCount = 0;				// Number of files in directory in working directory
    uint32_t ui32DirCount = 0;				// Number of directorys in working directory
    FATFS *psFatFs;							// File system object
    char *pcFileName;						// Current working dir/file name
    int nameLength=0;						// Lenght of pcFileName
    int dir_list_size=0;					// Number of bytes in dir_list for allocation
    int file_list_size = 0;					// Number of bytes in file_list for allocation
    char pucLfn[_MAX_LFN + 1];				// Local file/dir name buffer array
    g_sFileInfo.lfname = pucLfn;
    g_sFileInfo.lfsize = sizeof(pucLfn);

    // Free the space from the previews lists
    for(;counter<context.dir_count;counter++)
    {
    	free(&dir_list[counter]);				// Free each pointer memory space in dir_list
    }
    free(dir_list);								// Free dir_list pointer memory space
    for(counter=0;counter<context.file_count;counter++)
    {
      	free(&file_list[counter]);				// Free each pointer memory space in file_list
    }
    free(file_list);							// Free file_list pointer memory space
    counter = 0;								// Reset counter
    dir_list = malloc(sizeof(char*));			// Init dir_list
    file_list = malloc(sizeof(char*));			// Init file_list

    iFResult = f_opendir(&g_sDirObject, g_pcCwdBuf);     			// Open the current directory

    if(iFResult != FR_OK)
    {
    	context.error.SD_error = StringFromFResult(iFResult); 				// Check for error and return if there is a problem.
    	return;
    }

    for(;;)															// Enter loop to enumerate through all directory entries.
    {
    	// Read from directory and exit if any error occur
    	iFResult = f_readdir(&g_sDirObject, &g_sFileInfo); 			// Read an entry from the directory.
    	if(iFResult != FR_OK)
    	{
    		context.error.SD_error = StringFromFResult(iFResult);			// Check for error and return if there is a problem.
    		return;
    	}
        if(!g_sFileInfo.fname[0]) break;							//If the file name is blank, then this is the end of the listing.

        // Get directory or file name
        pcFileName = ((*g_sFileInfo.lfname)?g_sFileInfo.lfname:g_sFileInfo.fname);
        nameLength = strlen(pcFileName)+1;							// Get length of file name + /0

        if(g_sFileInfo.fattrib & AM_DIR)							// If the attribue is directory
        {
        	dir_list = realloc(dir_list,(nameLength+dir_list_size)*sizeof(char*));	// Reallocate memory space for whole dir_list
        	dir_list_size += nameLength;											// Increase the byte counter for dir_list
        	dir_list[ui32DirCount] = malloc(nameLength);							// Allocate memory space for single pointer in dir_list
        	if(dir_list == NULL || dir_list[ui32DirCount] == NULL)					// Check if allocation was succsessfull
        	{
        		context.error.SD_error = "Fehler beim erstellen der Ordner Liste !";
        		return;
        	}
            strcpy(dir_list[ui32DirCount],pcFileName);								// Copy file name to dir_list at local pointer
            ui32DirCount++;									   						// Increment the directory count
        }
        else														// Otherwise, it is a file, is not finished jet
        {
        	file_list = realloc(file_list,(nameLength+file_list_size)*sizeof(char*));	// Reallocate memory space for whole file_list
        	file_list_size += nameLength;												// Increase the byte counter for file_list
        	file_list[ui32FileCount] = malloc(nameLength);								// Allocate memory space for single pointer in file_list
        	if(file_list == NULL || file_list[ui32FileCount] == NULL)					// Check if allocation was succsessfull
        	{
        		context.error.SD_error = "Fehler beim erstellen der Dateien Liste !";
        	    return;
        	}
        	strcpy(file_list[ui32FileCount],pcFileName);								// Copy file name to file_list at local pointer
        	ui32FileCount++;															// Increment file counter
        }
    }
    context.file_count = ui32FileCount;								// Save the number of files
    context.dir_count = ui32DirCount;								// Save the number of directorys

    iFResult = f_getfree("/", (DWORD *)&ui32TotalSize, &psFatFs);   // Get the free space.
    if(iFResult != FR_OK)
    {
    	context.error.SD_error = StringFromFResult(iFResult); 		// Check for error and return if there is a problem.
    	return;
    }
    sys_info.free_space = (ui32TotalSize * psFatFs->free_clust / 2);// Save the overall free space
}

//*****************************************************************************
//
// This function implements the "cd" command.  It takes an argument that
// specifies the directory to make the current working directory.  Path
// separators must use a forward slash "/".  The argument to cd can be one of
// the following:
//
// * root ("/")
// * a fully specified path ("/my/path/to/mydir")
// * a single directory name that is in the current directory ("mydir")
// * parent directory ("..")
//
// It does not understand relative paths, so dont try something like this:
// ("../my/new/path")
//
// Once the new directory is specified, it attempts to open the directory to
// make sure it exists.  If the new path is opened successfully, then the
// current working directory (cwd) is changed to the new path.
//
//*****************************************************************************
void enterDir(char* dirName)
{
    //uint_fast8_t ui8Idx;

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

    if(strlen(context.dir_path)+strlen(dirName)+1 >sizeof(g_pcTmpBuf))	// Fully path must not be longer than temporary buffer
    {
    	context.error.SD_error = "Resulting path name is too long";		// Save error code
    	return;
    }
    else  strcpy(g_pcTmpBuf, strcat(context.dir_path,dirName));		// Copy the current working dir Path + dirName into a temporary buffer so it can be manipulated.

    // Try to open it to make sure it is valid.
    iFResult = f_opendir(&g_sDirObject, g_pcTmpBuf);

    // If it can't be opened, then it is a bad path. Inform the user and return.
    if(iFResult != FR_OK)
    {
    	context.error.SD_error = StringFromFResult(iFResult);
        return;
    }

    // Otherwise, it is a valid new path, so copy it into the CWD and save it to dir_path.
    else
    {
    	// Reallocate the memory for the whole path of the dir
    	context.dir_path = realloc(context.dir_path,strlen(context.dir_path) + strlen(dirName));
    	context.cur_dir = dirName;								// Current working dir is the opend dir
    	strcpy(context.dir_path, g_pcTmpBuf);					// Save full path from buffer to dir_path
    }

}

// This function implements the "cat" command.  It reads the contents of a file
// and prints it to the console.  This should only be used on text files.  If
// it is used on a binary file, then a bunch of garbage is likely to printed on
// the console.
void readFile(char* fileName)
{
    uint32_t ui32BytesRead;

    // 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_pcCwdBuf) + strlen(fileName) + 1 + 1 > sizeof(g_pcTmpBuf))
    {
    	context.error.SD_error = "Resulting path name is too long" ;
        return;
    }

    strcpy(g_pcTmpBuf, g_pcCwdBuf);	     					// Copy the current path to the temporary buffer so it can be manipulated.
    if(strcmp("/", g_pcCwdBuf))	strcat(g_pcTmpBuf, "/");	// If not already at the root level, then append a separator.
    strcat(g_pcTmpBuf, fileName);	 						// Now finally, append the file name to result in a fully specified file.

    iFResult = f_open(&g_sFileObject, g_pcTmpBuf, FA_READ); // Open the file for reading.

    if(iFResult != FR_OK)									// If there was some problem opening the file, then return an error.
    {
    	context.error.SD_error = StringFromFResult(iFResult);
    	return;
    }

    // Enter a loop to repeatedly read data from the file, 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.
        iFResult = f_read(&g_sFileObject, g_pcTmpBuf, sizeof(g_pcTmpBuf) - 1, (UINT *)&ui32BytesRead);

        // If there was an error reading, then print a newline and return the error to the user.
        if(iFResult != FR_OK)
        {
        	context.error.SD_error = StringFromFResult(iFResult);
            return;
        }

        // Null terminate the last block that was read
        g_pcTmpBuf[ui32BytesRead] = 0;


        // Print the last chunk of the file that was received.

        //UARTprintf("%s", g_pcTmpBuf);
    }
    while(ui32BytesRead == sizeof(g_pcTmpBuf) - 1);
}

// Like readFile, but the main loop is done in VS1063readFile and should only used in the VS1063 methode
void openFile(char* fileName)
{
    // First, check to make sure that the current path (CWD), plus a separator and trailing null, will all fit in the temporary buffer
    if(strlen(context.file_path)+strlen(fileName)+ 1 + 1 > sizeof(g_pcTmpBuf))
    {
    	context.error.SD_error = "Resulting path name is too long" ;
        return;
    }

    strcpy(g_pcTmpBuf, strcat(context.file_path,fileName));	 // Copy the current path to the temporary buffer so it can be manipulated.

    iFResult = f_open(&g_sFileObject, g_pcTmpBuf, FA_READ);  // Open the file for reading.

    if(iFResult != FR_OK)									 // If there was some problem opening the file, then return an error.
    {
    	context.error.SD_error = StringFromFResult(iFResult);
    	context.cur_file = NULL;							 // Reset the current file name
    }
    else
    {
    	context.cur_file = fileName;						 // If opening was successfull save the file name
    }
}

void closeFile(char* fileName)
{
	// First, check to make sure that the current path (CWD), plus a separator and trailing null, will all fit in the temporary buffer
	if(strlen(context.file_path)+strlen(fileName)+ 1 + 1 > sizeof(g_pcTmpBuf))
	{
		context.error.SD_error = "Resulting path name is too long" ;
	    return;
	}

	strcpy(g_pcTmpBuf, strcat(context.file_path,fileName));	    // Copy the current path to the temporary buffer so it can be manipulated.

	iFResult = f_close(&g_sFileObject);  // Close the file

	if(iFResult != FR_OK)				 // If there was some problem opening the file, then return an error.
	{
	    context.error.SD_error = StringFromFResult(iFResult);
	}

}

void SDinit()
{
   SysTickEnable();									// Enable system time interrupts
   SysTickPeriodSet(SysCtlClockGet() / 100); 	// Configure SysTick for a 100Hz interrupt.  The FatFs driver wants a 10 ms tick.

   iFResult = f_mount(0, &g_sFatFs);  			    // Mount the file system, using logical disk 0.

   // Is file system mounted or occured an error
   if(iFResult != FR_OK) context.error.SD_error = StringFromFResult(iFResult);

   dir_list=0;										// Init dir and file list
   file_list=0;
   getDirContent(); 								// Get the content of the root directory
}
