/****************************************************************************
	Kalkulon - A programmable calculator for programmers

	This file is part of the Kalkulon core.
	
	If you find this source code useful, your are encouraged to use it in your own projects.
	Please give me credit!

	COPYRIGHT by Juergen Holetzeck 2003 - 2008
	e-mail:		contact@kalkulon.de
	homepage:	www.kalkulon.de
****************************************************************************/

// this is a very simple command line interface for Kalkulon


#include "stdafx.h"

#include <iostream>
#include <sstream>
#include <string>

#include "kalkuloncore.h"

#ifdef USE_READLINE
	#include <readline/readline.h>
	#include <readline/history.h>
#endif


using namespace Kalkulon;

const char error[] = "KKC.EXE error:\n";
const int MAXLENGTH = 1024;

const char OPT_EVAL[] = "/e:";
const char PROMPT[] = "> ";

void getline(char *);
void help();

class MyCallback:public Kalkulon::Callback
{
	public:
		void print(const char * str, bool lf) {std::cout << str; if(lf) std::cout << '\n';};
};

int main(int argc, char* argv[])
{
	// check arguments
	if(argc == 2 && (!strcmp(argv[1],"/?") || !strcmp(argv[1],"-h"))) help();
	
	
	CRPNStackEval	evaluator;
	MyCallback		callbackOb;
	// register callback functions for printing
	evaluator.callbackOb = &callbackOb;
	
	if(argc >= 2)
	{
		int errorcode = 0;

		if(strncmp(argv[1], OPT_EVAL, sizeof(OPT_EVAL)-1))
		{
			// start script from command line		
			try
			{ 
				// convert command line arguments to KALKULON expression for ARGV = {"<argv[1]>", <argv[2]>,...};
				std::string expr = "ARGV={\"";
				expr+=argv[1];
				expr+='"';
				for(int i=2; i<argc; ++i)
				{
					expr+=", \""; expr+=argv[i]; expr+='\"';
				}
				expr+="};";

				// and parse it
				evaluator.parseEval(expr);

				const char* file=argv[1];
				if(evaluator.load(file)) 
				{
					std::cout << error << "could not open file <" << file << ">\n"; 
					errorcode = 1;
				}
			}
			catch(ErrBase &err)	{ std::cerr << error << err.GetErrorMessage() << std::endl; errorcode=2;}
			catch(...)          { std::cerr << error << "unknown exception while evaluation occured" << std::endl; errorcode=2;}
		}
		else
		{
			if(argc > 2) help();

			// parse command line as input
			const char* expr=argv[1]+sizeof(OPT_EVAL)-1;
			try	
			{ 
				DataObject result = evaluator.parseEval(std::string(expr));
				std::cout << result << std::endl;
			}
			catch(ErrBase &err)	{ std::cerr << err.GetErrorMessage() << std::endl; errorcode=2;}
			catch(...) { std::cerr << "unknown exception in parser occured" << std::endl; errorcode=2;}
		}
		return errorcode;
	}
	else
	{
		//////////////////////////////////////
		// start interactive console

		#ifdef USE_READLINE 
			rl_bind_key ('\t', rl_insert);
		#endif

		CArithParser	parser;
		char line[MAXLENGTH+1];

		// print title
		std::cout<< '\n' << version::title_str << " console " << version::version_str << " - " << version::copyright_str << " \nCR to quit!\n\n";

		// load 'autoload.k'
		try{ evaluator.load("autoload.k"); }
		catch(ErrBase &err)	{ std::cerr << err.GetErrorMessage() << std::endl;}
		catch(...)          { std::cerr << "unknown exception while autoload ocured" << std::endl;}
		
		getline(line);
		while(line[0])	
		{
			std::istringstream is(line);
			RPNStack_t rpn_stack;
			
			/////////////////////////////////////////////////////////////////////
			// run parser
			bool error = false;
			
			// parse file
			try	{ parser.parse(&is, &rpn_stack);	}
			catch(ErrBase &err)	{ std::cerr << err.GetErrorMessage() << std::endl; error=true; }
			catch(...) { std::cerr << "unknown exception in parser occured" << std::endl; error=true; }
			
			if(!error)
			{
#if _DEBUG
				// print parser results
				RPNStack_t::iterator it		= rpn_stack.begin();
				RPNStack_t::iterator itend	= rpn_stack.end();
				
				while(it != itend)
				{
					std::cout << *(it++) << " ";
				}
				std::cout << std::endl;
#endif
				
				/////////////////////////////////////////////////////////////////////
				// run evaluator
				try
				{
					DataObject result = evaluator.eval(rpn_stack);
					if(result.getDid() == DataObject::MESSAGE)
						std::cout << "\t\t\t" << result <<std::endl;
					else
						std::cout << "\t\t\t" << OUT_LIST << '[' << evaluator.getOutIdx() << "] = " << result <<std::endl;
				}
				catch(ErrBase &err)	{ std::cerr << err.GetErrorMessage() << std::endl; error=true; }
				catch(...) { std::cerr << "unknown exception in evaluator occured" << std::endl; error=true; }
			}
			
			getline(line);
		};
		return 0;
	}
}


void getline(char *buf)
{
	#ifdef USE_READLINE
		char *tmp;
		tmp = readline(PROMPT);
		if(strncmp(tmp, buf, MAXLENGTH)) add_history(tmp);	// only add new content
		strncpy(buf, tmp, MAXLENGTH);
		buf[MAXLENGTH]='\0';	
		free(tmp);
	#else
		std::cout<<PROMPT;
		std::cin.get(buf,MAXLENGTH);
		std::cin.ignore(); // delete CR
	#endif
}


void help()
{
	std::cout << '\n' << version::title_str << " console " << version::version_str << " - " << version::copyright_str << "\n\n";
	std::cout << "Usage:\n";
	std::cout << "1) kkc \t\t\tstarts interactive console\n";
	std::cout << "2) kkc " << OPT_EVAL << "\"<expr>\" \tevaluates <expr>, e.g. kkc " << OPT_EVAL << "\"3+4\"\n";
	std::cout << "3) kkc <file> [<ARGV[0]> ... <ARGV[n-1]>] \truns KALKULON script <file>\n\n";
	exit(1);
}
