/**
  * Author: Tom Mutdosch
  */

#include "Header.h"

Header::Header( int numOfFiles, char **fileNames ) {
    int startNum = 1;
    extension = "-cpp";  /** default extension */

    cout << "Header to Implementation File Generator" << endl;
    cout << "(c) 1999 Tom Mutdosch" << endl << endl;

    if ( numOfFiles <= 1 ) {
	cout << "Usage: header [-xxx] header_files" << endl;
	cout << "       xxx is the extension for C files (default: cpp)"<< endl;
	exit( 0 );
    }
	
    if ( *fileNames[1] == '-' ) {
	extension = fileNames[1];
	startNum = 2;
    }

    for ( int i = startNum; i < numOfFiles; i++ ) {
	/** initialize for each file */
	functionReturns.erase( functionReturns.begin(), functionReturns.end() );
	functions.erase( functions.begin(), functions.end() );
	className = "";
	blockComment = false;
	comment = false;

	getHeaderInfo( fileNames[i] );
	outputToCFile( fileNames[i] );
    }
}

/** 
  * input information from header file into data vectors
  * 
  */ 
void Header::getHeaderInfo( char *fileName ) {
    ifstream from( fileName );
    string input;
    string firstinput;
    bool nextIsClassName = false;  // is next keyword the classname? 

    if ( !from ) {
	cerr << "Cannot open file for readin: " << fileName << endl;
    }

    while ( getline( from, firstinput ) ) {
	input = stripWhiteSpace( firstinput );
	int lastIndex = 0;
	int i = 0;
	string last = "";
	string lastest = "";
	int lastNum = 0;
	int finalNum = 0;

	for ( i = 0; !checkForComment(input) && i < input.length()  ; i++ ) {
	    if ( input[i] == ' ' || i == input.length()-1 ) {
		string sub = input.substr( lastIndex, i-lastIndex );
		lastNum = i;

		if ( nextIsClassName ) {
   		    // remove inheritance ':' off of classname if necessary
		    if ( sub[sub.length()-1] == ':' ) {  
			className = sub.substr( 0, sub.length()-1 );
		    }
		    else {
			className = sub;
		    }
		    nextIsClassName = false;
		}

		if ( sub.compare( "class" ) == 0 ) {
		    nextIsClassName = true;
		}

		for ( int j = 0; j < sub.length(); j++ ) {
		    if ( sub[j] == '(' ) {
			// add the return values
			string returnValues = input.substr(0,finalNum);

			// add the function itself
			string tmp = input.substr( lastIndex, 
			    input.length()-lastIndex );  
			if ( tmp[tmp.length()-1] == ';' ) {
			    tmp[tmp.length()-1] = ' ';
			}
			else {
			    string append = "";
			    bool end = false;
			    while ( !end ) {
			        getline( from, append );
				input = stripWhiteSpace( firstinput );
				append = stripWhiteSpace( append );
				if ( append[append.length()-1] == ';' ) {
				    append[append.length()-1] = ' ';
				    end = true;
				}
				tmp += "\n\t" + append;
			    }
			}

			if ( tmp[0] == '*' || tmp[0] == '&' ) {
			    returnValues = returnValues + ' ' + tmp[0];
			    tmp = tmp.substr( 1, tmp.length()-1 );
			}
			else { 
			    returnValues = returnValues + ' ';
			}

			functions.push_back( tmp );
			functionReturns.push_back( returnValues );
		    }
		}
		lastest = sub;
		finalNum = lastNum;
		lastIndex = i+1;
	    }
	}
    }

}


/** 
  * output vector information to the C file
  * 
  */ 
void Header::outputToCFile( char *fileName ) {
    string st = (string)fileName;
    string outputName = st.substr( 0, st.length() - 1 );
    string ex = extension.substr( 1, st.length() - 1 );
    outputName = outputName + ex;
    char outName[outputName.length()+1];
    int index;

    for ( index = 0; index < outputName.length(); index++ )  {
	outName[index] = outputName[index];
    }
    outName[index] = '\0';


    ofstream to( outName ); 
    if ( !to ) {
	cerr << "Cannot write to file" << endl; 
    }
    to << "#include \"" << className << ".h\"" << endl;
    to << endl;
    for ( int i = 0; i < functions.size(); i++ ) {
	if ( functionReturns[i] != " " ) {
	    // only printout return values if it's not a constructor/deconst.
	    to <<  functionReturns[i];
	}
	to << className << "::" << functions[i] << "{" << endl;
	to << "}" << endl;
	to << endl;
    }
    cout << "...created " << outName << endl;
}

/**
  * check to see if this line is a comment (//) or starts a comment block 
  * or ends a comment block 
  * Returns true if the line is (in) a comment, false otherwisse
  */
bool Header::checkForComment( string input ) {
    int k;
    comment = false;

    for ( k = 0; ( input[k] == ' ' ||
	    input[k] == '\t' ) && k < input.length(); k++ );
    input = input.substr( k, input.length() - k);

    if ( input[0] == '/' && input[1] == '/' ) {
	comment = true;
    }

    if ( input[0] == '/' && input[1] == '*' ) {
	blockComment = true;
    }

    for ( k = 1; k < input.length(); k++ ) {
	if ( input[k-1] == '*' && input[k] == '/' ) {
	    blockComment = false;
	}
    }

    return ( comment || blockComment );
}

string Header::stripWhiteSpace( string input ) { 
    int k;

    /**
      * strip whitespace from beginning of line
      */
    for ( k = 0; ( input[k] == ' ' ||
	input[k] == '\t' ) && k < input.length(); k++ );
    input = input.substr( k, input.length() - k);

    /**
      * strip whitespace from beginning of line
      */
    for ( k = input.length()-1; 
	k >= 0 && ( input[k] == ' ' || input[k] == '\t' );k-- );
    input = input.substr( 0, k+1 );

    return input;
}




int main( int argc, char **argv ) {
    Header h( argc, argv );
}

