// VMParser.cpp : Contains the implementation of the VMParser class.
//Comment the following #include statement if you are not using Visual C++
#include "StdAfx.h"

#include <stdlib.h>
#include "VMParser.h"
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

/** VMParser
Parameter:
filename A string parameter that contains the pathname of the file to be parsed. 
Result:
Opens the file for reading.
Configures the input stream not to skip white spaces.
Initializes the member variables.
Returns:
Nothing
*/
VMParser::VMParser(const char* filename)
{
	inFile.open(filename,ios::in);
    if(inFile.fail()){ 
        cout << filename << " cannot be found\nPlease check whether you have entered the correct pathname\n";
        exit(0);
    }
    inFile.unsetf(ios::skipws);
    lineNumber=0;
	//arg1 is initialized to empty automatically
	arg2value = 0;
}

/** ~VMParser
Parameter:
None.
Result:
Closes the input file.
Returns:
Nothing
*/
VMParser::~VMParser(void)
{
	inFile.close();
}

/** hasMoreCommands
Parameter:
None.
Result:
Checks if there is more data to be read from the input file.
Returns:
true if there is more data to be read.
*/
bool VMParser::hasMoreCommands(){
      if (inFile.fail()) return false;
      else return true;
}

/** advance
Parameter:
None.
Result:
Reads in a single line of VM commands. Skips empty lines and discards the comments (marked by "//" ). Merges multiple white spaces (' ', '\t') into a single space.
The processed line is stored in 'currentLine', and contains a single space character between the command and each argument.
Calls parse() to parse the line.
Returns:
1 on success.
0 on end of file.
*/
int VMParser::advance()
{
	char ch = 0;
	arg1value.clear();
	arg2value = 0;
    currentLine.clear(); 
     
	 while (hasMoreCommands()) {
           inFile >> ch;
		   //read 1 character at a time, '\n'signals the end of the line
           if (ch=='\n') {
			     lineNumber++; //new line starts
                 if (currentLine.size() != 0) {
					break;
				 }
                 else continue;
           }
           if (ch!=' ' && ch!='\t' && ch!='\n' && ch!='\r')  {
				currentLine+=ch; 
		   }
           if (ch==' '||ch=='\t'){				
			   if (!currentLine.empty() && !(currentLine.at(currentLine.length()-1) == ' ')) currentLine += ' ';
		   }
           if (currentLine.size()>=2 && currentLine[currentLine.size()-1]=='/' && currentLine[currentLine.size()-2]=='/') {
			   //comment starts here
                currentLine.erase(currentLine.size()-2, 2);                
                inFile >> ch;
                while (ch!='\n') 
					inFile >> ch; 
				lineNumber++;
                if(currentLine.size()!=0) {					 
					break; //if there was a non-comment at the beginning of the line then we have to return it
				}                                     
		   }
	 }	 
	 while (!currentLine.empty() && currentLine.at(currentLine.length()-1) == ' ')
		 currentLine.pop_back(); //remove trailing white space
     if (currentLine.empty() || currentLine[0]=='\0') return 0;	 
	 parse();  
     return 1;
}

/** commandType()
Parameter:
None.
Result:
Iterates through cmdArray and finds the first entry that matches the beginning of currentLine. 
Returns:
The command type.
*/
CmdType VMParser::commandType()
{
	for (int i = 0; i < cmdMapSize; i++) {
		if (currentLine.find(cmdArray[i].cmd)==0) return cmdArray[i].type;
	}
	
	cout << lineNumber << ": Invalid command\n" ;                
    exit(0);
}

/** dump()
Parameter:
None.
Result:
Writes the command type and its parameters to the standard output.
Returns:
None.
*/

void VMParser::dump()
{
	cout << "Line " << lineNumber << ": " ;
	CmdType cmd = commandType();
	if (cmd > C_CALL) 
	{
		cout << "Invalid command type: " << cmd <<"\n";
		return;
	}
	else
	{
		cout << "\"" << cmdTypeStringArray[cmd] <<"\", ";
		switch (cmd)
		{
		case C_ARITHMETIC:
		case C_LABEL:
		case C_GOTO:
		case C_IF:
		case C_RETURN:
			cout << "\"" << arg1() << "\"\n";
			break;
		case C_PUSH:
		case C_POP:
		case C_CALL:
		case C_FUNCTION:
			cout << "\"" << arg1() << "\", \"" << arg2() << "\"\n";
			break;
		}	
		return;
	}
}

/** parse()
Parameter:
None.
Result:
Parses the member variable 'currentLine'. Assumes that there is a single whitespace between the command and between each argument (there can be up to 2 arguments). Fills in 'arg1value' (string) and 'arg2value' (integer).
Some examples for valid contents of currentLine and the desired contents of arg1value and arg2value would be:
---------------------------------------------------------------------
|        currentLine	-> desired contents							|
---------------------------------------------------------------------
| "push constant 2"		->	arg1value="constant", arg2value=2		|
| "call yourfunction 3" -> arg1value="yourfunction", arg2value=3	|
| "and"					-> arg1value="and", arg2value=0				|
| "label xyz"			-> arg1value="xyz"							|
---------------------------------------------------------------------
Returns:
Nothing
Hint: A good summary of the VM command syntax is given in Chapter 7.2. 
Hint: You can use the function 'atoi' to convert a 'const char*' to an integer.
Hint: To convert a string 's' to integer and store its value in 's_int' you would use 's_int = atoi(s.c_str())'
Hint: Refer to 'http://www.cplusplus.com/reference/string/string/' for string manipulation using the std::string C++ class.
*/
void VMParser::parse() {
    string arg = currentLine;//the Text to parse
	switch (commandType()) {
	//add your code here...	
	}
}