#include <services/services.h>
#include <services/fss/adi_fss.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <cli.h>

FILE *fp=NULL;
char fname[20];
char mode[5];
char snum[10];
char stype[5];
char wchar[1];

char argv[20][128];

/* private data and functions to process the command line arguments */
static char argv[20][128];
u32 GetArgs (char *s);
char *GetArg( u32 iarg );

/* weekday abbreviations */
static char *Weekday[] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat" };

typedef struct {
    char *Prefix;
    u32  PrefixLen;
    s32  Index;
} ARG_TYPE;

static s32 ProcessArgument(char *arg, ARG_TYPE *ArgTypes, u32 NumTypes, char **pValue );
static void printDirEntry(struct dirent *pDirEntry);
static void printVolumeInfo(FILE *fp, ADI_FSS_VOLUME_INFO *pPartition);

/*********************************************************************
    Function:       GetArgs
    Description:    Extracts arguments from command line
*********************************************************************/
section ("sdram0_bank3_app")
u32 GetArgs (char *s)
{
    u32 i,j=0,n,iarg=0, string=0, leading=1;
    u32 slen = strlen(s);
    for (i=0;i<slen;i++)
    {
        if (s[i] == ' ' || s[i] ==0)
        {
            if (!string && !leading) {
                    argv[iarg++][j] = '\0';
                    j=0;
            }
            else if (string) {
                    argv[iarg][j++] = s[i];
            }
        }
        else if (s[i] == '\"')
        {
            if (string) {
                argv[iarg++][j] = '\0';
                j=0;
                leading = 1;
                string=0;
            }
            else
            {
                string = 1;
            }
        }
        else
        {
            argv[iarg][j++] = s[i];
            leading = 0;
        }
    }
    argv[iarg++][j] = '\0';
    return iarg;
}

/*********************************************************************
    Function:       GetArg
    Description:    Retrieves the given command line argument
*********************************************************************/
section ("sdram0_bank3_app")
char *GetArg( u32 iarg )
{
    return &argv[iarg][0];
}

void SizeFile(void)
{
    if (fp) {
	    u32 curpos = ftell(fp);
	    fseek(fp,0,SEEK_SET);
	    fseek(fp,0,SEEK_END);
	    u32 size = ftell(fp);
	    printf("\nsize = %d\n",size);
	    fseek(fp,curpos,SEEK_SET);
    }
    else
    	printf("\nFile not open");
	printf("\n");
}

section ("sdram0_bank3_app")
void SetDate(char *arg)
{
    u32 i;
    u32 n = GetArgs(arg);
    if (n!=2) {
    	printf("\nUsage: date dow MMDDhhmmCCYY\n");
    	return;
    }
    	
    char *p = &argv[1][0];
    struct tm dat;
    char tmp[5];
    
	i = 0;
    strncpy(tmp,&p[i],2);
    tmp[2]=0;
    dat.tm_mon = atoi(tmp)-1;
    
    i+=2;
    strncpy(tmp,&p[i],2);
    tmp[2]=0;
    dat.tm_mday = atoi(tmp);
    
    i+=2;
    strncpy(tmp,&p[i],2);
    tmp[2]=0;
    dat.tm_hour = atoi(tmp);
    
    i+=2;
    strncpy(tmp,&p[i],2);
    tmp[2]=0;
    dat.tm_min = atoi(tmp);
    
    dat.tm_sec = 0;
    
    i+=2;
    strncpy(tmp,&p[i],4);
    tmp[4]=0;
    dat.tm_year = atoi(tmp) - 1900;
    
    for (i=0;i<7;i++)
    {
        if (!strncmp(argv[0], Weekday[i], 3) )
        {
		    dat.tm_wday = i;
            break;
        }
    }
    
 
    adi_rtc_SetDateTime ( &dat );
	/* Wait for 1Hz tick */
	while((*pRTC_ISTAT & 0x0004) != 0x0004);
	*pRTC_ISTAT = 0x0004;
    
    
    printf("\n");
}

section ("sdram0_bank3_app")
void ShowDate(FILE *fp)
{
	char str[80];
    struct tm Now;
    adi_rtc_GetDateTime ( &Now );
    Now.tm_isdst = FALSE;
	mktime( &Now );
	strftime(str,80,"%A %d %B %Y %I:%M:%S %p %Z",&Now);
	fprintf(fp,"%s\n",str);
}


section ("sdram0_bank3_app")
void CopyFile(char *arg)
{
    static char copy_buf[4096];
    u32 n = GetArgs(arg);
   
    if (n!=2) {
        printf("\nUsage: cp src dest");
        return;
    }
   
    printf("\n");
    char *file1 = &argv[0][0];
    char *file2 = &argv[1][0];
   
    FILE *src = fopen(file1,"rb");
    if (!src) {
        printf("failed to open source file, %s\n",file1);
        return;
    }
    FILE *dst = fopen(file2,"wb");
    if (!dst) {
        printf("failed to open destination file, %s\n",file2);
        fclose(src);
        return;
    }
    u32 nbytes,total=0;
    while (!feof(src))
    {
        u32 written;
        printf("copied %lu bytes\r",total);
      
        nbytes = fread(copy_buf, 1, sizeof(copy_buf), src);
        written=0;
       
        while (nbytes!=written)
        {
            written+=fwrite(copy_buf+written, 1, nbytes-written, dst);
        }
        total+=nbytes;
    }
    printf("\ndone.\n");
   
    fclose(src);
    fclose(dst);
}

section ("sdram0_bank3_app")
void RemoveFile(char *arg)
{
	u32 n = GetArgs(arg);
	if (n<1) {
	    printf("Usage: rm path\n");
	    return;
	}
    if ( remove(argv[0])==-1 )
    	printf("\nFailed to remove file, %s",argv[0]);
	printf("\n");
}

/*********************************************************************
    Function:       FormatVolume
    Description:    Formats a partition
*********************************************************************/
section ("sdram0_bank3_app")
void FormatVolume(char *arg)
{
    static ARG_TYPE FormatArgs[] = {
        { "-t=", 3, 0},
        { "-l=", 3, 1},
        { "-s=", 3, 2},
        { NULL,  0, 3}
    };

    u32 Result;
    u32 n = GetArgs(arg);
    s32 type;
    u32 size, i;
    char *label;
    char *pValue;
   
	ADI_FSS_FORMAT_DEF FormatDef;
	ADI_FSS_WCHAR label_text[11];
	u32 label_len = 11;

    printf("\n");
    if (n<1) {
        printf("Usage: format ident [t=<type> [l=<label> [s=<size-MB>]]]] \n");
        return;
    }

    type = 0;
    label = 0;
    label_len = 0;
    FormatDef.label = 0;
    FormatDef.label_len = 0;
    size = 0;

    for (i=0;i<n;i++) {
        switch ( ProcessArgument(argv[i],FormatArgs,sizeof(FormatArgs)/sizeof(ARG_TYPE),&pValue) ) 
        {
            case 0: 
                type = atoi(pValue);
                break;
            case 1:
                label = pValue;
            	label_len = strlen(pValue);
                break;
            case 2:
            	size = atoi(pValue);
                break;
            case 3:
            	FormatDef.ident = (ADI_FSS_VOLUME_IDENT)pValue[0];
                break;
        }
    }
    
	if (type==16) {
	    FormatDef.OptionMask = ADI_FSS_FMT_OPTION_VALUE(ADI_FSS_FSD_TYPE_FAT,1);
	} else if (type==32) {
	    FormatDef.OptionMask = ADI_FSS_FMT_OPTION_VALUE(ADI_FSS_FSD_TYPE_FAT,2);
	} else {
		printf("Invalid type, must be 16 or 32\n");
		return;
	} 
	
    /* Copy label to ADI_FSS_WCHAR array (first 11 chars only) */
    if (label) {
    	for (i=0;i<(label_len);i++) {
    	    label_text[i] =label[i]; 
    	}
    	for (;i<11;i++) {
    	    label_text[i] =' '; 
    	}
        FormatDef.label = &label_text[0];
        FormatDef.label_len = label_len;
    }
    
    
	FormatDef.VolumeDef.VolumeSize = size*2048;

	FormatDef.VolumeDef.FileSystemType = ADI_FSS_FSD_TYPE_FAT;
    
    printf("Formatting %c as FAT %d ... ", FormatDef.ident, type );
    
    /* Instruct the FSS to reformat the volume */
    Result = adi_fss_Control( ADI_FSD_CMD_FORMAT_VOLUME, (void*)&FormatDef );

    if (Result==ADI_FSS_RESULT_SUCCESS) {
	    printf("Done\n");    	
    }
    else {
        printf("Format failed\n");
    }
}

/*********************************************************************
    Function:       FormFullPathName
    Description:    returns the fully qualified path for a given file
*********************************************************************/
section ("sdram0_bank3_app")
char *FormFullPathName( char *path, char *fname, char *fullpath)
{
    
    if (fname[0]=='/' || fname[2]=='/' ) {
        strcpy(fullpath, fname);
    } else {
        strcpy(fullpath, path);
        if ( path[strlen(path)-1]!='/' )
        {
            strncat(fullpath, "/", 1);
        }
        strcat(fullpath, fname);
    }
    return fullpath;
}

/*********************************************************************

    Function:       printVolumeInfo

    Description:    Writes the definition of a single mounted volume

*********************************************************************/
section ("sdram0_bank3_app")
static void printVolumeInfo(FILE *fp, ADI_FSS_VOLUME_INFO *pPartition)
{
	u32 i;
	u32 mbytes = pPartition->size;
	char label[40], type[40];
	for (i=0;pPartition->label[i]!=0;i++) {
	    label[i] = pPartition->label[i];
	}
	label[i] = '\0';
	for (i=0;pPartition->type[i]!=0;i++) {
	    type[i] = pPartition->type[i];
	}
	type[i] = '\0';
	fprintf(fp,"%c: \t%s\t%s\t%6d MB",pPartition->Ident, label, type, mbytes);
	if (pPartition->status)
	    fprintf(fp,"\tMounted\n");
	else
	    fprintf(fp,"\tNot Mounted\n");
}

/*********************************************************************

    Function:       ShowVolumes

    Description:    Writes the definitions of a all mounted volume

*********************************************************************/
section ("sdram0_bank3_app")
void ShowVolumes(FILE *fp)
{
    ADI_FSS_VOLUME_INFO Volume;
    
    Volume.Index=0;
    while (adi_fss_Control(
        ADI_FSS_CMD_GET_VOLUME_INFO,
        (void*)&Volume)==ADI_FSS_RESULT_SUCCESS)
    {
        printVolumeInfo(fp,&Volume);
        Volume.Index++;
    }
}

/*********************************************************************

    Function:       printDirEntry

    Description:    Writes the details of a single directory entry

*********************************************************************/
section ("sdram0_bank3_app")
static void printDirEntry(struct dirent *pDirEntry)
{
	static char str[80];

	if (pDirEntry->d_type == DT_DIR)
	    printf("d");
	else
	    printf("f");
	
	printf("\t%10d", pDirEntry->d_size);
	    
	strftime(str,80,"%b %d %Y %H:%M:%S",&pDirEntry->DateModified);
	printf("  %s",str);
	printf("\t%s ", pDirEntry->d_name);
	printf("\n");
}

/*********************************************************************

    Function:       ListDir

    Description:    Lists the contents of a directory to stdout

*********************************************************************/
section ("sdram0_bank3_app")
void ListDir(char *name)
{
    struct dirent *pDirEntry;
    	
	DIR *pDir = opendir(name);
	if (!pDir)
	    return;
	    
	do {
    	    pDirEntry = readdir(pDir);
	    if (pDirEntry)
	        printDirEntry(pDirEntry);
	} while (pDirEntry);
	
	closedir(pDir);
}


/*********************************************************************

    Function:       DisplayImageFile

    Description:    Displays an image or list of images to the LCD

*********************************************************************/
section ("sdram0_bank3_app")
void DisplayImageFile(char *arg)
{    
    static ARG_TYPE DisplayImageArgs[] = {
        { "-r=", 3, 0},
        { "-d=", 3, 1},
        { NULL,  0, 2}
    };

    char* filepath;
    static char cwd[256];
    u32 RepeatCount=1, Duration=2, i, n;
    char *pValue;
    
    printf("\n");
    
    n = GetArgs(arg);
    if (n<1) {
    	printf("\nUsage: view [-r=<repeat-count>] <image-file-or-list>\n");
    	return;
    }
    
    /* process the arguments */
    for (i=0;i<n;i++) {
        switch ( ProcessArgument(argv[i],DisplayImageArgs,sizeof(DisplayImageArgs)/sizeof(ARG_TYPE),&pValue) ) 
        {
            /* Assign the slide show repeate count */
            case 0: 
                RepeatCount = atoi(pValue);
                break;
            /* Assign the Duration to display each image */
            case 1:
                Duration = atoi(pValue);
                break;
            /* Assign the file name */
            case 2:
                filepath = &argv[i][0];
                break;
        }
    }
    
    
    /* Get the current working directory */
    getcwd(cwd, 256);
    
	/* Set Viewer parameters */
	//ConfigureViewer( cwd, RepeatCount, Duration);
	
	/* Display the file (or list of files) on the LCD screen */
	//ViewImages(filepath);
}

/*********************************************************************
    Function:       ProcessArgument
    Description:    Parses a single argument in a command
*********************************************************************/
section ("sdram0_bank3_app")
static s32 ProcessArgument(char *arg, ARG_TYPE *ArgTypes, u32 Numtypes, char **pValue )
{
    u32 i;
    s32 result = -1;
    
    for (i=0;i<Numtypes;i++) 
    {
        if (ArgTypes[i].Prefix==NULL)
        {
            *pValue = &arg[0];
            result = ArgTypes[i].Index;
            break;
        }
        else  if ( !strncmp(ArgTypes[i].Prefix,arg,ArgTypes[i].PrefixLen) ) 
        {
            *pValue = &arg[ArgTypes[i].PrefixLen];
            result = ArgTypes[i].Index;
            break;
        } 
    }
    
    return result;
}


