/****************************************************************************
	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
****************************************************************************/


// commands.cpp: Implementierung der Klasse commands.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <algorithm>
#include <fstream>
#include "commands.h"
#include "RPNStackEval.h"
#include "version1.h"
#include "ErrBase.h"

#if USE_MANAGED
#using <mscorlib.dll>
#using <System.dll>
#endif

#if USE_GLIBC
#include <time.h>
#include <sys/time.h>
#endif

#if ANDROID
int AndroidExec(const char* command, const char* param);
#endif

using namespace Kalkulon;

double cmd::Size(const DataObject& dob)
{
	switch(dob.getDid())
	{
		case DataObject::FNUMBER:
			return 1;
		case DataObject::STRING:
			return dob.getPtrStrValue()->size();
		case DataObject::LIST:
			return dob.getPtrListValue()->size();
		default:
			return 0;
	}
}


double cmd::Type(const DataObject& dob)
{
	switch(dob.getDid())
	{
		/* reserved
		case DataObject::INUMBER:
			return 1;
		*/
		case DataObject::FNUMBER:
			return 2;
		case DataObject::STRING:
			return 3;
		case DataObject::LIST:
			return 4;
		case DataObject::PUREFUNC:
			return 5;
		default:
			return 0;
	}
}


DataObject cmd::Back(const DataObject& dob)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(dob.getPtrListValue()->size()) 
					return dob.getPtrListValue()->back();
				break;
			}
		case DataObject::STRING:
			{
				size_t size = dob.getPtrStrValue()->size();
				if(size)
				{
					std::string s;
					s+=(*dob.getPtrStrValue())[size-1];
					return s;
				}
				break;
			}
		default:
			// return dob itself
			break;
	}
	return dob;
}


DataObject cmd::Front(const DataObject& dob)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(dob.getPtrListValue()->size())
					return dob.getPtrListValue()->front();
				break;
			}
		case DataObject::STRING:
			{
				if(dob.getPtrStrValue()->size())
				{
					std::string s;
					s+=(*dob.getPtrStrValue())[0];
					return s;
				}
				break;
			}
		default:
			// return dob itself
			break;
	}
	return dob;
}


DataObject cmd::PushBack(const DataObject& dob, const DataObject& elem)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
		{
			DataObject tmp(dob);
			tmp.getPtrListValue()->push_back(elem);
			return tmp;
		}
		case DataObject::STRING:
			if(elem.getDid() == DataObject::STRING)
			{
				DataObject tmp(dob);
				*tmp.getPtrStrValue() += *elem.getPtrStrValue();
				return tmp;
			}
			throw ErrMathInternal("Push","TYPE MISMATCH");
		default:
			throw ErrMathInternal("Push","TYPE MISMATCH");
	}
}


DataObject cmd::PushFront(const DataObject& dob, const DataObject& elem)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
		{
			DataObject tmp(dob);
			tmp.getPtrListValue()->insert(tmp.getPtrListValue()->begin(),elem);
			return tmp;
		}
		case DataObject::STRING:
			if(elem.getDid() == DataObject::STRING)
			{
				DataObject tmp(elem);
				*tmp.getPtrStrValue() += *dob.getPtrStrValue();
				return tmp;
			}
			throw ErrMathInternal("Push","TYPE MISMATCH");
		default:
			throw ErrMathInternal("Push","TYPE MISMATCH");
	}
}


DataObject cmd::PopBack(const DataObject& dob)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
			if(dob.getPtrListValue()->size())
			{
				DataObject tmp(dob);
				tmp.getPtrListValue()->pop_back();
				return tmp;
			}
			break;	
		case DataObject::STRING:
			{
				size_t size = dob.getPtrStrValue()->size();
				if(size) 
					return dob.getPtrStrValue()->substr(0,size-1);
				break;
			}
		default:
			throw ErrMathInternal("Pop","TYPE MISMATCH");
	}
	return dob;	
}


DataObject cmd::PopFront(const DataObject& dob)
{
	switch(dob.getDid())
	{
		case DataObject::LIST:
			if(dob.getPtrListValue()->size())
			{
				DataObject tmp(dob);
				tmp.getPtrListValue()->erase(tmp.getPtrListValue()->begin());
				return tmp;
			}
			break;	
		case DataObject::STRING:
			{
				size_t size = dob.getPtrStrValue()->size();
				if(size)
					return dob.getPtrStrValue()->substr(1,size-1);
				break;
			}
		default:
			throw ErrMathInternal("Pop","TYPE MISMATCH");
	}
	return dob;	
}


DataObject  cmd::Insert(const DataObject& dob, const DataObject& idx_dob, const DataObject& elem)
{
	unsigned int idx = (unsigned int) idx_dob;
	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(idx>dob.getPtrListValue()->size()) throw ErrMathInternal("Insert","OUT OF RANGE");
				DataObject tmp(dob);
				if(elem.getDid() == DataObject::LIST)
				{
					tmp.getPtrListValue()->insert(tmp.getPtrListValue()->begin()+idx, elem.getPtrListValue()->begin(), elem.getPtrListValue()->end());
				}
				else
					tmp.getPtrListValue()->insert(tmp.getPtrListValue()->begin()+idx, elem);
				return tmp;
			}
		case DataObject::STRING:
			{
				if(idx>dob.getPtrStrValue()->size()) throw ErrMathInternal("Insert","OUT OF RANGE");
				if(elem.getDid() == DataObject::STRING)
				{	
					DataObject tmp(dob);
					tmp.getPtrStrValue()->insert(idx, *elem.getPtrStrValue());
					return tmp;
				}
				throw ErrMathInternal("Insert","TYPE MISMATCH");
			}
		default:
			throw ErrMathInternal("Insert","TYPE MISMATCH");
	}
}


DataObject  cmd::Replace(const DataObject& dob, const DataObject& idx_dob, const DataObject& elem)
{
	unsigned int idx = (unsigned int) idx_dob;
	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(idx>dob.getPtrListValue()->size()) throw ErrMathInternal("Replace","OUT OF RANGE");
				DataObject tmp(dob);
				if(elem.getDid() == DataObject::LIST)
				{
					size_t elem_size= elem.getPtrListValue()->size();
					if(idx+elem_size>tmp.getPtrListValue()->size()) tmp.getPtrListValue()->resize(idx+elem_size);
					for(size_t i=0; i<elem_size; ++i) (*tmp.getPtrListValue())[idx+i] = elem[i];
				}
				else
				{	
					if(idx+1>tmp.getPtrListValue()->size()) tmp.getPtrListValue()->resize(idx+1);
					(*tmp.getPtrListValue())[idx] = elem;
				}
				return tmp;
			}
		case DataObject::STRING:
			{
				if(idx>dob.getPtrStrValue()->size()) throw ErrMathInternal("Replace","OUT OF RANGE");
				DataObject tmp(dob);
				if(elem.getDid() == DataObject::STRING)
				{	
					tmp.getPtrStrValue()->replace(idx, elem.getPtrStrValue()->size(), *elem.getPtrStrValue());
					return tmp;
				}
				break;
			}
		default:
			if(idx==0) return elem;
			break;
	}
	throw ErrMathInternal("Replace","TYPE MISMATCH");
}


DataObject  cmd::Sub1(const DataObject& dob, const DataObject& beg)
{
	unsigned int b = (unsigned int) beg;

	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				size_t e = dob.getPtrListValue()->size();
				if(b>e) throw ErrMathInternal("Sub","OUT OF RANGE");
				DataObject tmp(DataObject::LIST);
				tmp.getPtrListValue()->resize(e-b);
				std::copy(dob.getPtrListValue()->begin()+b, dob.getPtrListValue()->begin()+e, tmp.getPtrListValue()->begin());
				return tmp;
			}
		case DataObject::STRING:
			{
				size_t e = dob.getPtrStrValue()->size();
				if(b>e) throw ErrMathInternal("Sub","OUT OF RANGE");
				return dob.getPtrStrValue()->substr(b,e-b);
			}
		default:
			if(b==0) return dob;
			break;
	}
	throw ErrMathInternal("Sub","TYPE MISMATCH");
}


DataObject  cmd::Sub2(const DataObject& dob, const DataObject& beg, const DataObject& end)
{
	unsigned int b = (unsigned int) beg;
	unsigned int e = (unsigned int) end;
	if(b>e)	throw ErrMathInternal("Sub","OUT OF RANGE");

	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(e>dob.getPtrListValue()->size()) throw ErrMathInternal("Sub","OUT OF RANGE");
				DataObject tmp(DataObject::LIST);
				tmp.getPtrListValue()->resize(e-b);
				std::copy(dob.getPtrListValue()->begin()+b, dob.getPtrListValue()->begin()+e, tmp.getPtrListValue()->begin());
				return tmp;
			}
		case DataObject::STRING:
			{
				if(e>dob.getPtrStrValue()->size()) throw ErrMathInternal("Sub","OUT OF RANGE");
				return dob.getPtrStrValue()->substr(b,e-b);
			}
		default:
			if(b==0 && e==1) return dob;
			break;
	}
	throw ErrMathInternal("Sub","TYPE MISMATCH");
}


DataObject  cmd::Erase1(const DataObject& dob, const DataObject& beg)
{
	unsigned int b = (unsigned int) beg;

	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(b>=dob.getPtrListValue()->size()) throw ErrMathInternal("Erase","OUT OF RANGE");
				DataObject tmp(dob);
				tmp.getPtrListValue()->erase(tmp.getPtrListValue()->begin()+b);
				return tmp;
			}
		case DataObject::STRING:
			{
				if(b>=dob.getPtrStrValue()->size()) throw ErrMathInternal("Erase","OUT OF RANGE");
				DataObject tmp(dob);
				tmp.getPtrStrValue()->erase(tmp.getPtrStrValue()->begin()+b);
				return tmp;
			}
		default:
			throw ErrMathInternal("Erase","TYPE MISMATCH");
	}
}


DataObject  cmd::Erase2(const DataObject& dob, const DataObject& beg, const DataObject& end)
{
	unsigned int b = (unsigned int) beg;
	unsigned int e = (unsigned int) end;
	if(b>e)	throw ErrMathInternal("Erase","OUT OF RANGE");

	switch(dob.getDid())
	{
		case DataObject::LIST:
			{
				if(e>dob.getPtrListValue()->size()) throw ErrMathInternal("Erase","OUT OF RANGE");
				DataObject tmp(dob);
				tmp.getPtrListValue()->erase(tmp.getPtrListValue()->begin()+b, tmp.getPtrListValue()->begin()+e);
				return tmp;
			}
		case DataObject::STRING:
			{
				if(e>dob.getPtrStrValue()->size()) throw ErrMathInternal("Erase","OUT OF RANGE");
				DataObject tmp(dob);
				tmp.getPtrStrValue()->erase(b,e-b);
				return tmp;
			}
		default:
			throw ErrMathInternal("Erase","TYPE MISMATCH");
	}
}


double cmd::Load(CRPNStackEval *p, std::string file)
{
	return p->load(file.c_str());
}


DataObject cmd::ClearAll(CRPNStackEval *p)
{
	p->clearAll();
	DataObject dob("ClearAll", DataObject::MESSAGE);
	return dob;
}

#ifdef _MSC_VER
 #pragma warning( disable : 4100 ) // unreferenced formal parameter
#endif
DataObject	cmd::Version(CRPNStackEval *p)
{
	DataObject dob("");
	*dob.getPtrStrValue() += version::title_str;
	*dob.getPtrStrValue() += " ";
	*dob.getPtrStrValue() += version::version_str;
    *dob.getPtrStrValue() += " - ";
    *dob.getPtrStrValue() += version::compiler_version_str;
    return dob;
}
#ifdef _MSC_VER
 #pragma warning( default : 4100 )
#endif

DataObject cmd::LinePos(const DataObject& file_dob, const DataObject& offset_dob)
{
	unsigned int offset = (unsigned int) offset_dob;
	if(file_dob.getDid() != DataObject::STRING) throw ErrMathInternal("LinePos","TYPE MISMATCH");

    int line=1, col=0;
    std::ifstream in(file_dob.getPtrStrValue()->c_str(), std::ios::binary);
    if(in)
    {
        char ch;
        while(in && offset)
        {
            ch = (char)in.get(); ++col; --offset;
            // test for '\n', '\r\n' and '\r'
            if( (ch == '\n' ) || (ch == '\r' && in.peek() != '\n'))
            {
                ++line;
                col = 0;
            }
        }
    }
    else
    {
        line=col=-1;
    }
    in.close();

	DataObject tmp(DataObject::LIST);
	tmp.getPtrListValue()->push_back(DataObject(line)); 
	tmp.getPtrListValue()->push_back(DataObject(col));
	return tmp;
}


DataObject cmd::Print(CRPNStackEval *p, const DataObject& dob)
{
	if(p->callbackOb)
		p->callbackOb->print((static_cast<std::string>(dob)).c_str(), false);
	return dob;
}


double cmd::PrintLn0(CRPNStackEval *p)
{
	if(p->callbackOb)
		p->callbackOb->print("", true);
	return 0;
}


DataObject cmd::PrintLn1(CRPNStackEval *p, const DataObject& dob)
{
	if(p->callbackOb)
		p->callbackOb->print((static_cast<std::string>(dob)).c_str(), true);
	return dob;
}


std::string cmd::Sprintf(std::string format, const DataObject& dob)
{
	return dob.sprintf(format);
}


DataObject	cmd::FullForm(const DataObject& dob)
{
	return DataObject(dob.formattedString(DataObject::FULLFORM));
}


double cmd::Error(std::string reason)
{
	throw ErrUserDef(reason);
	//return 0;
}


std::string cmd::SetNumFormat(std::string format_string)
{
	return DataObject::setNumberFormat(format_string);
}

std::string cmd::SetStdFormat()
{
	return DataObject::setNumberFormat(DataObject::STD_FORMAT);
}

std::string cmd::SetHexFormat()
{
	return DataObject::setNumberFormat("0x%x");
}

std::string cmd::SetBinFormat()
{
	return DataObject::setNumberFormat("0b%b");
}

std::string cmd::SetSciFormat()
{
	return DataObject::setNumberFormat("%.12e");
}

double	cmd::SetRecursionLimit(int rec_limit)
{
	return CRPNStackEval::SetRecursionLimit(rec_limit);
}

DataObject cmd::Eval(CRPNStackEval *p, std::string expr)
{
	return p->parseEval(expr);
}


DataObject  cmd::ForEach1(CRPNStackEval *p, const DataObject& list, const DataObject& purefunc)
{
	if(list.getDid()     != DataObject::LIST)     throw ErrMathInternal("ForEach","TYPE MISMATCH arg1");
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("ForEach","TYPE MISMATCH arg2");
	
	return p->ForEach1(list.getPtrListValue(), purefunc.getPtrRPNStack());
}


DataObject  cmd::ForEach2(CRPNStackEval *p, const DataObject& list1, const DataObject& list2, const DataObject& purefunc)
{
	if(list1.getDid()    != DataObject::LIST)     throw ErrMathInternal("ForEach","TYPE MISMATCH arg1");
	if(list2.getDid()    != DataObject::LIST)     throw ErrMathInternal("ForEach","TYPE MISMATCH arg2");
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("ForEach","TYPE MISMATCH arg3");
	if(list1.getPtrListValue()->size() != list2.getPtrListValue()->size()) throw ErrMathInternal("ForEach","SIZE MISMATCH");

	return p->ForEach2(list1.getPtrListValue(), list2.getPtrListValue(), purefunc.getPtrRPNStack());
}


DataObject  cmd::Table1(CRPNStackEval *p, const DataObject& end, const DataObject& purefunc)
{
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("Table","TYPE MISMATCH arg2");

	double e=(double) end;
	if(0==e)
	{
		// special case: empty list
		return DataObject(DataObject::LIST);
	}
	else
	{		
		double s=1;
		if(e<0) s=-1;
		return p->Table(s, e, 1, purefunc.getPtrRPNStack());
	}
	
}


DataObject  cmd::Table2(CRPNStackEval *p, const DataObject& start, const DataObject& end, const DataObject& purefunc)
{
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("Table","TYPE MISMATCH arg3");
	return p->Table((double) start, (double) end, 1, purefunc.getPtrRPNStack());
}


DataObject  cmd::Table3(CRPNStackEval *p, const DataObject& start, const DataObject& end, const DataObject& delta, const DataObject& purefunc)
{
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("Table","TYPE MISMATCH arg4");
	return p->Table((double) start, (double) end, (double) delta, purefunc.getPtrRPNStack());
}


DataObject  cmd::Fold(CRPNStackEval *p, const DataObject& list, const DataObject& first, const DataObject& purefunc)
{
	if(list.getDid()     != DataObject::LIST)     throw ErrMathInternal("Fold","TYPE MISMATCH arg1");
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("Fold","TYPE MISMATCH arg3");
	return p->Fold(list.getPtrListValue(), first, purefunc.getPtrRPNStack());
}


DataObject  cmd::FoldList(CRPNStackEval *p, const DataObject& list, const DataObject& first, const DataObject& purefunc)
{
	if(list.getDid()     != DataObject::LIST)     throw ErrMathInternal("FoldList","TYPE MISMATCH arg1");
	if(purefunc.getDid() != DataObject::PUREFUNC) throw ErrMathInternal("FoldList","TYPE MISMATCH arg3");
	return p->FoldList(list.getPtrListValue(), first, purefunc.getPtrRPNStack());
}


DataObject cmd::ReadData(const DataObject& file)
{
	if(file.getDid() != DataObject::STRING) throw ErrMathInternal("ReadData","TYPE MISMATCH arg1");
	
	DataObject list(DataObject::LIST);
	std::ifstream in(file.getPtrStrValue()->c_str());

	while(in) 
	{
		std::string line;
		std::getline(in, line);
		if(in) list.getPtrListValue()->push_back(line);
	}
	in.close();

	return list;
}


DataObject cmd::WriteData(const DataObject& file, const DataObject& list)
{
	if(file.getDid() != DataObject::STRING) throw ErrMathInternal("WriteData","TYPE MISMATCH arg1");
	if(list.getDid() != DataObject::LIST) throw ErrMathInternal("WriteData","TYPE MISMATCH arg2");

	std::ofstream out(file.getPtrStrValue()->c_str());
	if(out)
	{
		for(size_t i=0; i<list.getPtrListValue()->size(); ++i)
		{
			out << (std::string) list[i] << '\n';
		}

		out.close();
		return DataObject(0);	// ok
	}
	else
		return DataObject(1);	// error
}


DataObject cmd::ReadBinData0(const DataObject& file)
{
	if(file.getDid() != DataObject::STRING) throw ErrMathInternal("ReadBinData","TYPE MISMATCH arg1");

	DataObject list(DataObject::LIST);
	std::ifstream in(file.getPtrStrValue()->c_str(), std::ios_base::binary);

	while(in)
	{
		int byte=in.get();
		if(in) list.getPtrListValue()->push_back(byte);
	}
	in.close();

	return list;
}


DataObject cmd::ReadBinData1(const DataObject& file, const DataObject& beg, const DataObject& end)
{
	if(file.getDid() != DataObject::STRING) throw ErrMathInternal("ReadBinData","TYPE MISMATCH arg1");

	unsigned int b = (unsigned int) beg;
	unsigned int e = (unsigned int) end;
	if(b>e)	throw ErrMathInternal("ReadBinData","OUT OF RANGE");
	
	DataObject list(DataObject::LIST);
	std::ifstream in(file.getPtrStrValue()->c_str(), std::ios_base::binary);
	if(in)
	{
		in.seekg(b);
		while(in && b++<e)
		{
			int byte=in.get();
			if(in) list.getPtrListValue()->push_back(byte);
		}
		in.close();
	}

	return list;
}


DataObject cmd::WriteBinData(const DataObject& file, const DataObject& list)
{
	if(file.getDid() != DataObject::STRING) throw ErrMathInternal("WriteBinData","TYPE MISMATCH arg1");
	if(list.getDid() != DataObject::LIST) throw ErrMathInternal("WriteBinData","TYPE MISMATCH arg2");

	std::ofstream out(file.getPtrStrValue()->c_str(), std::ios_base::binary);
	if(out)
	{
		for(size_t i=0; i<list.getPtrListValue()->size(); ++i)
		{
			out.put((char)list[i]);
		}

		out.close();
		return DataObject(0);	// ok
	}
	else
		return DataObject(1);	// error
}
//////////////////////////////////////////////////////////////////////////////
// platform dependent stuff
double cmd::Beep(double freq, double dur)
{
	#if USE_WIN32	
		::Beep((DWORD) freq, (DWORD) dur);
		return 0;
	#elif USE_MANAGED
	try{
		System::Console::Beep((int) freq, (int) dur);
	}
	catch(...)
	{
		return -1;
	}
		return 0;
	#else
		return -1;
	#endif
}


double cmd::Exec1(std::string app)
{
	std::string s("");
	return cmd::Exec2(app, s);
}


double cmd::Exec2(std::string app, std::string param)
{
	#if USE_WIN32
		HINSTANCE error=::ShellExecuteA(NULL,"open",app.c_str(),param.c_str(),NULL,SW_SHOWNORMAL);
		#ifdef _MSC_VER
		 #pragma warning( disable : 4311) // pointer truncation from 'HINSTANCE' to 'int'
		#endif
		
		return (reinterpret_cast<int> (error)<=32);
		
		#ifdef _MSC_VER
		 #pragma warning( default : 4311)
		#endif 
	#elif USE_MANAGED
		int stat = 0;
		System::Diagnostics::Process^ proc = gcnew System::Diagnostics::Process();
		proc->StartInfo->FileName  = gcnew System::String(app.c_str());
		proc->StartInfo->Arguments = gcnew System::String(param.c_str());
		proc->StartInfo->Verb = "open";
		try{	
			proc->Start();
		}
		catch(...)
		{
			stat = 1;
		}
		delete proc;
		return stat;
    #elif USE_GLIBC && !ANDROID
		// start external application in background, so system() returns immediately
		int error = system((app+" "+param+" &").c_str());
		return error;
    #elif ANDROID
		int error = AndroidExec(app.c_str(), param.c_str());
		return error;
    #else
		return -1;
	#endif
}


double cmd::GetTicks()
{
	#if USE_WIN32
		return ::GetTickCount();
	#elif USE_MANAGED
		return (int) System::Environment::TickCount;
	#elif USE_GLIBC
		struct timeval tv;
		if(!gettimeofday (&tv, NULL))
			return (tv.tv_sec*1000 + tv.tv_usec/1000);
		else
			return -1;
	#else
		return -1;
	#endif
}
