#include <iostream>
using namespace std;
#include <windows.h>
#include <string>
#include "common.h"
#include <fstream>
#include "md5.h"

void setScreenPos(int row, int col)
{
	position pos;
	pos.row=row;
	pos.col=col;
	setScreenPos(pos);
}

void setScreenPos(position pos)
{
	COORD coord;
	coord.X=short(pos.col);
	coord.Y=short(pos.row);
	SetConsoleCursorPosition ( GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void setColor(int foreground, int background)
{
	int color=foreground;
	color+=(16*background);

	HANDLE hConsole;
	hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(hConsole,color);
}

void drawBorder(position topLeft, unsigned int width, unsigned int height, string header)
{
	setColor (black,black);

	setColor(BRyellow,red);
	unsigned int i;
	setScreenPos(topLeft);

	if ( header.length() > width-2) 
	{
		width=header.length()+2;
		topLeft.col=(SCREEN_WIDTH/2) - (width/2);
		setScreenPos(topLeft);
	}

	// display top
	cout << char(201); // ╔
	for ( i=0; i < width; i++ )
		cout << char(205); // ═
	cout << char(187); // ╗

	// display header
	if ( header != "" )
	{
		int center=topLeft.col + (width/2) - (header.length())/2;
		setScreenPos(topLeft.row,center);
		cout << char(181);
		setColor(BRwhite,black);
		cout << header;
		setColor(BRyellow,red);
		cout << char(198);
	}

	// display sides
	for ( i=0; i < height; i++ )
	{
		topLeft.row++;
		setScreenPos(topLeft);
		cout << char(186); // ║
		setScreenPos(topLeft.row,topLeft.col+width+1);
		cout << char(186); // ║
	}
	topLeft.row++;	

	// display bottom
	setScreenPos(topLeft);
	cout << char(200); // ╚
	for ( i=0; i < width; i++ )
		cout << char(205); // ═
	cout << char(188); // ╝
}
void removeBorder(position topLeft, unsigned int width, unsigned int height)
{
	// used to clear portions of the screen

	setColor (black,black);

	unsigned int i;
	setScreenPos(topLeft);


	// clear all rows
	for ( i=0; i < height+2; i++ )
	{
		setScreenPos(topLeft);
		for ( unsigned int j=0; j < width+2; j++ )
			cout << " ";
		topLeft.row++;
	}

}

void setStatus(string statusText)
{
	// displays a line of text on the status bar at the bottom of the screen

	setScreenPos(SCREEN_HEIGHT-1,1);
	setColor(BRwhite,BRblack);
	cout << statusText;
	for ( int i=statusText.length(); i < SCREEN_WIDTH-2; i++ ) cout << " ";
	setColor(BRgreen,black);
}

void alert(string message, int FGcolor, int BGcolor, string header)
{
	// be alert, the world needs more lerts.
	// this function pops up a message box in the center of the screen

	char key=NULL;
	position topLeft;
	topLeft.row=SCREEN_HEIGHT/2;
	topLeft.col=(SCREEN_WIDTH/2) - (message.length()/2);
	
	drawBorder(topLeft,message.length(),1,header);
	setScreenPos(topLeft.row+1,topLeft.col+1);
	setColor(FGcolor,BGcolor);
	cout << message;

	do
	{
		key=_getch();
	} while ( key != 13 && key != 27 );

	removeBorder(topLeft,message.length(),1);
	
}

sysConfig::sysConfig(string configFile)
{
	// a set of system settings that can be passed from function to function
	// right now includes only manager's (md5 hashed) password, and the shutdown flag

	myConfigFile=configFile;
	ifstream inFile;
	inFile.open ( configFile.c_str() );
	if ( inFile )
	{
		inFile >> managerPassword;
	} else {
		alert ("Warning!! Unable to load system configuration!");
		exit(0);
	}

	inFile.close();
	shutdownFlag=false;
}

bool sysConfig::isManagerPassword(string tryPassword)
{
	if ( md5(tryPassword) == managerPassword && managerPassword.length() >= MIN_PASSWORD_LENGTH )
		return true;
	else
		return false;
}

bool sysConfig::saveNewPassword(string newPass)
{
	newPass=md5(newPass);
	ofstream outFile;
	bool success=false;

	outFile.open(myConfigFile.c_str(),ios::binary);
	if ( outFile )
	{
		outFile.clear();
		outFile.seekp(ios::beg);
		outFile.write(newPass.c_str(),newPass.size());
		success=true;
		managerPassword=newPass;
	}

	outFile.close();
	return success;
}

bool sysConfig::getShutdownFlag() { return shutdownFlag; }
void sysConfig::setShutdownFlag() { shutdownFlag=true; }

bool safeFilename(string filename)
{
	/** 
	because customers are stored in seperate files named for their driver's license #
	it's possible some unscrupulous person could enter a value like "c:\autoexec.bat" or something
	so this function checks to see if a filename is safe to use.
	**/

	bool safe=true;
	bool charOkay=false;

	for ( unsigned int i=0; i < filename.size(); i++ )
	{
		if ( (filename[i] >= '0' && filename[i] <= '9' ) || 
			 (filename[i] >= 'a' && filename[i] <= 'z' ) ||
			 (filename[i] >= 'A' && filename[i] <= 'Z' ) ||
			 (filename[i] == '-') || (filename[i] == '_') )
			charOkay=true;
		else
			charOkay=false;
		if ( !charOkay ) 
		{
			safe=false;
			break;
		}
	}

	return safe;
}

bool doesFileExist(string filename)
{
	bool result=false;

	ifstream testFile;
	testFile.open(filename.c_str());
	if ( testFile ) result=true;	// the compiler likes this better than 'result=testFile'

	testFile.close();

	return result;
}

time_t makeTimestamp(int month, int day, int year, int hour, int minute, int second)
{
	time_t tstamp;
	tm *myTM=new tm();
	myTM->tm_mday=day;
	myTM->tm_mon=month-1;
	myTM->tm_year=year;
	if ( myTM->tm_year >= 1900 ) myTM->tm_year -= 1900;

	/** vc++ timestamp system sucks (thanks MS, I hate you)
	basically all times before the epoch (1/1/1970) are non-representable, unlike every other language on earth (php for example)
	they seem to be more worried about the 2038 problem.  Therefore it is possible to put dates of some rediculously long time
	in the future (such as the year 2200) but not < 1970 
	so the workaround for this is to add 100 years to every timestamp before convertion, and then subtract it later when it's read back
	**/
	myTM->tm_year+=100;
	
	myTM->tm_hour=hour-1;	// adding years (any amount) also adds one hour (go figure), compensate for that.
	myTM->tm_min=minute;
	myTM->tm_sec=second;
	myTM->tm_isdst=0;

	tstamp=mktime(myTM);

	delete myTM;

	return tstamp;
}

string makeDate(time_t timeStamp, bool includeTime)
{
	tm *myTM=new tm();
	localtime_s(myTM, &timeStamp);
	
	string dateString=asctime(myTM);

	int year;
	string strYear;

	// split into seperate entities so that it can be reformatted.
	vector<string> tmp=split(dateString,' ');
	assert (tmp.size() >=5 );

	strYear=tmp[4];
	year=atoi(strYear.c_str());
	year-=100;	// undo the +100 years added in makeTimestamp
	strYear=numToString(year);

	dateString=tmp[1];
	dateString.append(" ");
	dateString.append(tmp[2]);
	dateString.append(" ");
	dateString.append(strYear);

	if ( includeTime )
	{
		dateString.append(", ");
		dateString.append(tmp[3]);
	}

	return dateString;
}

time_t add100Years(time_t tStamp)
{
	// because of the whole snafu with the timestamps, all timestamps need to be set 100 years in the future
	// normally you could use time(0) to generate a timestamp for right now.  This function will add 100 years to that time
	// or any other timestamp given to this function

	tm tmp;
	localtime_s(&tmp,&tStamp);
	tmp.tm_year+=100;
	tmp.tm_hour-=1;
	tStamp=mktime(&tmp);
	return tStamp;
}