/*
  pwd   - Print working directory.
  cd    - Change directory,
  ls    - List working directory.
  mkdr  - Make new directory.
  rm    - Delete file/directory.
*/

#include <SD.h>
#include <strTools.h>
#include <lilParser.h>
#include "SDTools.h"
#include "filePath.h"


#define SD_CS   4     // SD chip select pin number. Change to suit your setup.


enum pathPrefix { setRoot, upOne, upNPath, fullPath, relPath };  // Prefixes people typically use for entering path paramiters.

enum commands {   noCommand,  // ALWAYS start with noCommand. Or something simlar.
                  printWD,    // The rest is up to you. help would be a good one. Have it list
                  changeDir,  // What the other commands are, how they work and what they do.
                  listDir,
                  makeDir,
                  deleteFile
              };          // Our list of commands.

lilParser   ourParser;        // The parser object.
filePath    wd;


void setup() {

	Serial.begin(9600);
	Serial.println("Hello! This lets you explore, and do some");
  Serial.println("limited ediing of files on an SD card.");                                                   //
	if (!SD.begin(SD_CS)) {                             // If we can not initialze a SD drive.
		Serial.println("No SD Drive!");                   // Tell the user.
		while (1);                                        // Just stop here.
	}
	ourParser.addCmd(printWD, "pwd");                   // Add pwd command  [ If they type "pwd" they mean printWD ]
	ourParser.addCmd(changeDir, "cd");                  // Add cd command
	ourParser.addCmd(listDir, "ls");                    // Add ls command
	ourParser.addCmd(makeDir, "mkdr");                  // Add mkdr command
	ourParser.addCmd(makeDir, "mkdir");                 // We'll take it either way.
	ourParser.addCmd(deleteFile, "rm");                 // Add rm command.
                                                      //
  showComs();                                         // Lets see the list of commands.
  wd.setPath("/");                                    // Initial path setting.
  showCurs();                                         // Default curser setting.
}

void showCurs(void) {

  Serial.print(wd.getPath());
  Serial.print(" > ");
}

// Your loop where it parses out all your typings.
void loop(void) {
	
	char  inChar;
	int   command;
  bool  curs;

	if (Serial.available()) {                                       // If serial has some data..
    curs = true;                                                  // Show the cursor after..
		inChar = Serial.read();                                       // Read out a charactor.
		Serial.print(inChar);                                         // If using development machine, echo the charactor.
		command = ourParser.addChar(inChar);                          // Try parsing what we have.
		switch (command) {                                            // Check the results.
			case noCommand    : curs = false;                  break;   // Nothing to report, move along.
			case printWD      : Serial.println(wd.getPath());  break;   // Easy peasy! Just print wd out.
			case listDir      : listDirectory();               break;   // Print out a listing of the working directory.
			case makeDir      : makeDirectory();               break;   // See if we can create a directory in the working directory.
			case changeDir    : changeDirectory();             break;   // Try changing directorys.
			case deleteFile   : deleteItem();                  break;   // Delete a directory or file.
			default           : showComs();                    break;   // No idea. Show them the list.
		}
    if (curs) showCurs();
	}
}



/************************************************************************************
*************************************************************************************

                     Now the list of command handlers you call from
                     your main loop() when commands are parsed.

*************************************************************************************
*************************************************************************************/

void showComs(void) {

  Serial.println("Possible commands..");
  Serial.println("pwd           show working directory.");
  Serial.println("ls            list items in the working directory.");
  Serial.println("cd *path*     Can be fullpath starting at '/' or");
  Serial.println("              reletive path startig with just a");
  Serial.println("              directoty. Or .. to go back to");
  Serial.println("              parent directory.");
  Serial.println("mkdir *path*  Can be fullpath starting at '/' or"); 
  Serial.println("              reletive path startig with just a");
  Serial.println("              directoty.");
  Serial.println("rm *path*     Can be fullpath starting at '/' or"); 
  Serial.println("              reletive path starting with just a");
  Serial.println("              directoty. Will delete files or");
  Serial.println("              directories. Directories do NOT need");
  Serial.println("              to be empty, this will empty them.");
  Serial.println("              NOTE: Be careful with this. Emptying");
  Serial.println("              directories can eat a lot of RAM if");
  Serial.println("              they are nested deeply.");
}


bool checkFile(pathItem*	item) {

  if (item->name[0]=='.') return false;
  return true;
}


//enum pathPrefix { setRoot, upOne, upNPath, fullPath, relPath };
//
//      /         ->  Set to root.
//      ..        ->  Go up one.
//      ..path    ->  Go up, then relative path.
//      /path     ->  Full path.
//      path      ->  Relative folder.
//
pathPrefix decodePrefix(const char* param) {

  if (!strcmp(param,"/")) return setRoot;                   // Just "/" alone means root. setRoot.
  else if (param[0]=='/') return fullPath;                  // Starts with '/' + more..   fullPath.
  else if (!strcmp(param,"..")) return upOne;               // Just ".." means go up one. upOne.
  else if (param[0]=='.' && param[1]=='.') return upNPath;  // Starts with ".." + more..  upNPath.
  else return relPath;                                      // Default, relitive path.    relPath.
}


// [ls] Lists all the files in the working direcotory.
void listDirectory(void) {

  pathItem*		trace;

  if (ourParser.numParams()) {                                  // If they typed in something past the command.
    Serial.println("Sorry, ls takes no params. Ignoring..");    // Let 'em know..
  }                                                             //
  if (wd.getPathType()==fileType) {                             // If this is pointing to a file..? 
    Serial.println("Sorry, this is a file, not a directory.");  // Just wrong, we can't list a file.
    return;                                                     // Just walk away..
  } else {                                                      // Else, not a file. Good!
    wd.refreshChildList();                                      // Make sure we have an up to date child list..
    if (wd.numChildItems()==0) {                                // Wait, no children to list?
      Serial.println("This directory is empty.");               // Tell the user.
      return;                                                   // And walk away.
    } else {                                                    // Else, we DO have kids to list.
      trace = wd.childList;																	    // Grab a pointer to the first child.
      while(trace) {																						// While we have a non-NULL pointer..
        if (checkFile(trace)) {																	// Pass this child through the crucible of the user's filter function.
          Serial.print(trace->getName());                       // If passed, print it's name.
          if (trace->getType()!=fileType) {                     // If it's a directory..
            Serial.print("/");                                  // Add the "I'm a direcotry" slash to it.
          }                                                     // 
          Serial.println();                                     // Line feed..
        }                                                       //
        trace = (pathItem*)trace->dllNext;											// Jump to the next item on the list.
      }
    }
  }
}


// [mkdir] Create a new directory in the working directory using typed in parameter.
void makeDirectory(void) {

  tempStr param;
  int     numBytes;
  char*   pathBuff;

  pathBuff = NULL;                                // Make SURE these start at NULL.
  if (ourParser.numParams()==1) {                 // If they typed in something past the command.
    param.setStr(ourParser.getParamBuff());       // We get the first parameter. Should be the new folder's name/path.
    switch (decodePrefix(param.getStr())) {       // Let's see what they are asking for..
      case setRoot  :                             // Create root..
      case upOne    :                             // Pop up a direcoty..
      case upNPath  :                             // Pop up and go elsewhere..
        Serial.println("Illegal command");        // Not going to deal with any of those for now.
      break;                                      // And we're off.
      case fullPath :                             // Full path, we can do that.
        if(!SD.mkdir(param.getStr())) {           // If we can't make the direcotry..
          Serial.println("File error.");          // Tell 'em..
        }                                         //
      break;                                      // In any case, we're off.
      case relPath  :                             // Relitive path..
        numBytes = wd.numPathBytes();             // We need our wd path..
        numBytes = numBytes + param.numChars();   // And the param .
        if (resizeBuff(numBytes,&pathBuff)) {     // If we can get the RAM..
          strcpy(pathBuff,wd.getPath());          // Stuff in our wd path.
          strcat(pathBuff,param.getStr());        // Add the param text.
          if(!SD.mkdir(pathBuff)) {               // If we can't make the direcotry..
            Serial.println("File error.");        // Tell 'em..
          }                                       //
          resizeBuff(0,&pathBuff);                // recycle the RAM.
        }                                         //
      break;                                      // In any case, we're off.
    }
  }
}


// [cd] Change the working directory. (See prefix commands above.)
void changeDirectory(void) {

  tempStr       param;
  tempStr       savedPath;
 
  savedPath.setStr(wd.getPath());                           // Save the path we have, just in case.
  if (ourParser.numParams()==1) {                           // If they typed in a parameter (name/path).
    param.setStr(ourParser.getParamBuff());                 // We grab the name.
    switch(decodePrefix(param.getStr())) {                  // Decide on how the param is prefixed..
      case setRoot  :                                       // Just set to root..
        //Serial.println("Setting root");
        wd.setPath("/");                                    // Easy peasy!
      break;                                                //
      case upOne    :                                       // Want to go up one deirectory..
        //Serial.println("upOne");
        if (wd.getPathType()==rootType) {                   // If we're at the root already..
          Serial.println("Sorry, already at root.");        // Send error. Can't one up more root.
        } else {                                            // Else, not root?
          wd.popItem();                                     // Go ahead and pop off the item.
        }                                                   //
      break;                                                //
      case upNPath  :                                       // Up then, relitive path..
        //Serial.println("upNPath");
        if (wd.getPathType()==rootType) {                   // If we're at the root already..
          Serial.println("Sorry, already at root.");        // Send error. Can't one up more root.
        } else {                                            // Else, not root?
          wd.popItem();                                     // Go ahead and pop off the item.
          if (!wd.addPath(&(param.getStr()[2]))) {          // If we can't set this concatinated path..
            wd.setPath(savedPath.getStr());                 // Put it all back..
            Serial.println("Path not found.");              // Error.
          } else {                                          // Else, we got a path. Check it out.
            if (wd.getPathType()==fileType) {               // If this is pointing to a file..
              wd.setPath(savedPath.getStr());               // Put it all back..
              Serial.println("Path is not a directory.");   // Working DIRECTORY can't be a file.
            }                                               //
          }                                                 //
        }                                                   //
      break;                                                // Done!
      case fullPath :                                       // Full path..
        //Serial.println("fullPath");
        if (!wd.setPath(param.getStr())) {                  // If we can't set this path..
          wd.setPath(savedPath.getStr());                   // Put it all back..
          Serial.println("Path not found.");                // Error.
        } else {                                            // Else, we got a path, check it out.
          if (wd.getPathType()==fileType) {                 // If this is pointing to a file..
            wd.setPath(savedPath.getStr());                 // Put it all back..
            Serial.println("Path is not a directory.");     // Working DIRECTORY can't be a file.
          }                                                 //
        }                                                   //
      break;                                                // Skipping off..
      case relPath  :                                       // Relitive path..
         //Serial.println("relPath");
        if (wd.addPath(param.getStr())) {                   // If we can add this relitive path bit..
          if (wd.getCurrItem()->getType()==fileType) {      // If this is pointing to a file..
            wd.setPath(savedPath.getStr());                 // Put it all back..
            Serial.println("Path is not a directory.");     // Let 'em know.
          }                                                 //
          //Serial.print("Added:");
          //Serial.println(param.getStr());
        } else {                                            // Else, couldin't find the file.
          wd.setPath(savedPath.getStr());                   // Put it all back..
          Serial.println("Path not found.");                // Let 'em know.
        }                                                   //
      break;                                                // We're off..
    }
  }
}


//[rm] Delete a file or directory..
void deleteItem(void) {

  tempStr param;
  tempStr savedPath;
 
  savedPath.setStr(wd.getPath());             // Save the path we have, just in case.
  if (ourParser.numParams()==1) {             // If they typed in a parameter (name).
    param.setStr(ourParser.getParamBuff());   // We grab the name.
    switch(decodePrefix(param.getStr())) {    // Decide on how the param is prefixed..
      case setRoot  :                         // Just set to root..
      case upOne    :                         // Want to go up one deirectory..
      case upNPath  :                         // Up then, relitive path..
      case fullPath :                         // Full path..
        Serial.println("Illegal command.");   // We don't do those with delete.
      break;                                  // Skipping off..
      case relPath  :                         // Relitive path..
        if (wd.addPath(param.getStr())) {     // If we can add this relitive path bit..
          if(!wd.deleteCurrentItem()) {       // If we failed to delete the item..
            Serial.println("File error.");    // Something went wrong.
          }                                   //
        } else {                              // Else, couldin't find the file.
          Serial.println("Path not found.");  // Let 'em know.
        }                                     //
        wd.setPath(savedPath.getStr());       // Put it all back..
      break;                                  // Enough!
    }
  }
}