//#include "movie.h"
#include "common.h"
#include "customer.h"
#include <iostream>
#include <iomanip>
#include <ctime>
//#include <conio.h>
#include <fstream>
#include "menu.h"
using namespace std;

movie::movie(string setIDNum)
{
	genre="";
	rated="";
	title="";
	numCopies=0;
	rentals=0;	
	dateAdded=add100Years(time(0));
	dateReleased=0;
	id=setIDNum;
}

movie::~movie() { 
	// destructor
	//cout << "deleting movie";
}

int movie::getStock()
{	
	int stock=0;
	for ( unsigned int i=0; i < copyStatus.size(); i++ )
		if ( copyStatus[i].length() < 1 ) stock++;

	return stock;	
}

bool movie::initNewMovie()
{

	const int NUM_FIELDS=7;
	const int FIELD_WIDTH=20;
	const int MIN_DATA_LENGTH=1;
	const int DATA_WIDTH=SCREEN_WIDTH-FIELD_WIDTH-9;

	enum fields
	{	TITLE, GENRE, RATED, RELMONTH, RELDAY, RELYEAR, STOCK };

	string fieldName[NUM_FIELDS] = 
	{	"Title", 
		"Genre",
		"Rated", 
		"Release Month",
		"Release Day",
		"Release Year",
		"Stock Quantity"
	};

	position p;
	char choice=NULL;
	int releaseMonth, releaseDay, releaseYear;

	do {
		p.row=2; p.col=3;
		drawBorder(p,SCREEN_WIDTH-9,SCREEN_HEIGHT-9,"Create new movie");

		setColor(BRwhite,BRblack);
		p.col++;
		
		for ( int i=0; i < NUM_FIELDS; i++ )
		{
			p.row++; setScreenPos(p);
			cout << right << setw(FIELD_WIDTH) << fieldName[i];
		}

		p.row=2; p.col=4+FIELD_WIDTH;
		setColor(BRgreen,black);
		for ( int i=0; i < NUM_FIELDS; i++ )
		{
			p.row++; setScreenPos(p);
			string thisFieldData="";
			thisFieldData=standardPrompt(BRgreen,black,DATA_WIDTH);

			// check length
			if ( thisFieldData.length() < MIN_DATA_LENGTH )
			{
				i--; 
				setScreenPos(p);
				p.row--;
				for ( int f=0; f < DATA_WIDTH; f++ ) cout << ' ';
				continue;
			}
			switch ( i )
			{
				case TITLE:
					title=titleize(thisFieldData);
					break;
				case GENRE:
					genre=ucfirst(thisFieldData);
					break;
				case RATED:
					rated=ucase(thisFieldData);
					break;
				case RELMONTH:
					releaseMonth=atoi(thisFieldData.c_str());
					break;
				case RELDAY:
					releaseDay=atoi(thisFieldData.c_str());
					break;
				case RELYEAR:
					releaseYear=atoi(thisFieldData.c_str());
					break;
				case STOCK:
					numCopies=atoi(thisFieldData.c_str());
					break;
			};

		}
		
		dateReleased=makeTimestamp(releaseMonth,releaseDay,releaseYear);

		p.row=SCREEN_HEIGHT-4; p.col=(SCREEN_WIDTH/2)-12;
		drawBorder(p,24,1);
		p.row++; p.col++;
		setScreenPos(p);
		setColor(BRyellow,BRblue);

		cout << left << setw(24) << "[D]one, [R]edo, [A]bort";
		choice = toupper(_getch());

		p.row=SCREEN_HEIGHT-4; p.col=(SCREEN_WIDTH/2)-12;
		removeBorder(p,24,1);
		p.row=2; p.col=3;
		removeBorder(p,SCREEN_WIDTH-9,SCREEN_HEIGHT-9);

	} while ( choice == 'R' );

	// generate an ID number
	id=makeIDnum();

	// set up the copyStatus vector
	copyStatus.empty();
	for ( int i=0; i < numCopies; i++ )
		copyStatus.push_back("");

	if ( choice == 'D' )
		return true;
	else
		return false;
}

void movie::load(ifstream &inFile)
{
	string strLine;
	string varb, val;
	int pos;
	
	while ( !inFile.eof() )
	{
		getline(inFile,strLine);
		strLine=trim(strLine);
		if ( "</" == left(strLine,2) ) break;	// hit end of my record
		pos=strpos(strLine,"=");
		if ( pos > 0 )
		{
			varb=lcase(left(strLine,pos));
			val=strLine.substr(pos+1,strLine.length()-pos);
			if (	  "title"		 == varb )	title=val;
			else if ( "genre"		 == varb )	genre=val;
			else if ( "rated"		 == varb )	rated=val;
			else if ( "rentals"		 == varb )	rentals=atoi(val.c_str());
			else if ( "numcopies"	 == varb )	
			{
				numCopies=atoi(val.c_str());
				// set up the copy status vector with x empty strings
				copyStatus.empty();
				for ( int i=0; i < numCopies; i++ )
					copyStatus.push_back("");
			}
			else if ( "dateadded"	 == varb )	dateAdded=stringToLongLong(val);
			else if ( "datereleased" == varb )	dateReleased=stringToLongLong(val);
			else if ( "id"			 == varb )  id=val;
			else if ( "copystatus"	 == varb )  
			{
				vector<string> cs=split(val);
				for ( unsigned int i=0; i < cs.size(); i++ )
					copyStatus[i]=cs[i];
			}
			else if ( "waitlist"	 == varb )	
			{
				// load waitlist queue
				vector<string> wl=split(val);
				for ( unsigned int i=0; i < wl.size(); i++ )
					waitlist.push(wl[i]);
			} // end load waitlist

		} // end this line is a variable/value pair
	} // end while not eof

	if ( id.length() < 1 ) 
		id=makeIDnum();	// I was unable to load ID from file, generate a new one

} // end load function

string movie::makeIDnum()
{
	string theID;

	time_t theTime = time(0);
	theID=numToString(theTime);
	//theID.append("-");
	int r=rand()%10000; r+= 10000;
	theID.append(numToString(r));
	return theID;
}
void movie::save(ofstream &outFile)
{
	// serialize the waitlist into a string, ',' delimited
	string wl="";

	queue<string> tmpWaitlist=waitlist;
	
	while ( tmpWaitlist.size() > 0 )
	{
		if ( wl.size() > 0 ) wl.append(",");	// this is not the 1st entry, add a coma
		wl.append(tmpWaitlist.front());
		tmpWaitlist.pop();
	}

	// serialize the copystatus array into a string
	string cs="";
	for ( int i=0; i < numCopies; i++ )
	{
		if ( i > 0 ) cs.append(",");
		cs.append(copyStatus[i]);
	}

	outFile << "<movie>\n";
	outFile << "	Title=" << title << endl;
	outFile << "	Genre=" << genre << endl;
	outFile << "	Rated=" << rated << endl;
	outFile << "	Rentals=" << rentals << endl;
	outFile << "	NumCopies="	<< numCopies << endl;
	outFile << "	DateAdded=" << dateAdded << endl;
	outFile << "	DateReleased=" << dateReleased << endl;
	outFile << "	ID=" << id << endl;
	outFile << "	CopyStatus=" << cs << endl;
	outFile << "	Waitlist=" << wl << endl;
	outFile << "</movie>\n";

} // end save function

int movie::getWaitlistSize()
{
	int size=0;
	queue<string> temp=waitlist;
	while ( !temp.empty() )
	{
		if ( temp.front().length() > 0 )
			size++;
		temp.pop();
	};
	return size;
}

// the following functions are used to allow the building of search trees
// sorted by various fields

bool movie::operator >  (movie &right)
{
	bool result=false;

	switch ( sortField )
	{
		case SORT_FIELD_TITLE:
			result=lcase(title) > lcase(right.title);
			break;
		case SORT_FIELD_GENRE:
			result=lcase(genre) > lcase(right.genre);
			break;
		case SORT_FIELD_STOCK:
			result=getStock() > right.getStock();
			break;
		case SORT_FIELD_RENTALS: 
			result=rentals > right.rentals;
			break;
		case SORT_FIELD_WAITLISTCOUNT:
			result=getWaitlistSize() > right.getWaitlistSize();
			break;
		case SORT_FIELD_DATEADDED:
			result=dateAdded > right.dateAdded;
			break;
		case SORT_FIELD_DATERELEASED:
			result=dateReleased > right.dateReleased;
			break;
		case SORT_FIELD_ID:
			result=id > right.id;
			break;
		default:
			alert ("Tried to sort by an unknown field");
			result=false;
			break;
	};
	return result;
}

bool movie::operator == (movie &right)
{
	bool result=false;
	switch ( sortField )
	{
		case SORT_FIELD_TITLE:
			result=lcase(title) == lcase(right.title);
			break;
		case SORT_FIELD_GENRE:
			result=lcase(genre) == lcase(right.genre);
			break;
		case SORT_FIELD_STOCK:
			result=getStock() == right.getStock();
			break;
		case SORT_FIELD_RENTALS: 
			result=rentals == right.rentals;
			break;
		case SORT_FIELD_WAITLISTCOUNT:
			result=getWaitlistSize() == right.getWaitlistSize();
			break;
		case SORT_FIELD_DATEADDED:
			result=dateAdded == right.dateAdded;
			break;
		case SORT_FIELD_DATERELEASED:
			result=dateReleased == right.dateReleased;
			break;
		case SORT_FIELD_ID:
			result=id == right.id;
			break;
		default:
			alert ("Tried to sort by an unknown field");
			result=false;
			break;
	};
	return result;	
}


void movie::displayList(int secondField)
{

	const int MAX_TITLE_WIDTH=60;

	string dispTitle=displayTitle(title);
	if ( dispTitle.length() > MAX_TITLE_WIDTH-3 ) dispTitle=left(dispTitle,MAX_TITLE_WIDTH-3)+"...";

	cout << left << setw(MAX_TITLE_WIDTH) << dispTitle;
	cout << right << setw(SCREEN_WIDTH-MAX_TITLE_WIDTH-2);

	switch ( secondField )
	{
		case SORT_FIELD_STOCK:
			cout << getStock();
			break;
		case SORT_FIELD_RENTALS:
			cout << rentals;
			break;
		case SORT_FIELD_WAITLISTCOUNT:
			cout << getWaitlistSize();
			break;
		case SORT_FIELD_DATEADDED:
			cout << makeDate(dateAdded);
			break;
		case SORT_FIELD_DATERELEASED:
			cout << makeDate(dateReleased);
			break;
		case SORT_FIELD_ID:
			cout << id;
			break;
		default:
			cout << ucfirst(genre);
			break;
	};
}

void movie::displayFull(customer *theCustomer, bool managerAccess)
{
	position topLeft;
	topLeft.row=8;	topLeft.col=5;
	drawBorder(topLeft,SCREEN_WIDTH-12,10,"Movie Details");
	
	const int FIELD_WIDTH=SCREEN_WIDTH-12;
	setColor(BRgreen,black);

	topLeft.col++;
	topLeft.row++; setScreenPos(topLeft);
	string dispTitle=displayTitle(title);
	if ( dispTitle.length() > 46 ) dispTitle=left(dispTitle,43)+"...";

	cout << left << setw(FIELD_WIDTH) << "               Title: "+dispTitle;
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "               Genre: "+genre;
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "               Rated: "+rated;
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "   Number of Rentals: "+numToString(rentals);
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "   Quantity in Stock: "+numToString(getStock());
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "Date Added to system: "+makeDate(dateAdded);
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "   Film Release Date: "+makeDate(dateReleased);
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "                ID #: "+id;

	//topLeft.row++; setScreenPos(topLeft);
	//cout << left << setw(FIELD_WIDTH) << " ";

	setColor(BRyellow,black);
	topLeft.row++; setScreenPos(topLeft);
	cout << left << setw(FIELD_WIDTH) << "[ESC] Close Details ";

	topLeft.row++; setScreenPos(topLeft);
	if ( theCustomer != NULL )
	{
		if ( !(theCustomer->isGuestAccount()) )
			cout << left << setw(FIELD_WIDTH) << "[C] Checkout movie, [W] Check Waitlist Position";
		else
			cout << left << setw(FIELD_WIDTH) << " ";
	}
	else if ( managerAccess )
		cout << left << setw(FIELD_WIDTH) << "[C] View checkout list, [W] View waitlist";
	else
		cout << left << setw(FIELD_WIDTH) << " ";


	char choice=toupper(_getch());

	if ( managerAccess )
	{
		if ( 'W' == choice )
			displayWaitlist();
		else if ( 'C' == choice )
			displayCheckoutList();
	} else if ( theCustomer != NULL && !(theCustomer->isGuestAccount() ) )
	{
		if ( choice == 'C' )
		{
			// handle checking out movies here
			checkout(theCustomer);
		} else if ( choice == 'W' )
		{
			// check waitlist position
			int pos=getQueuePosition(theCustomer->getDriversLicense());
			string msg;
			if ( pos < 0 )
				msg="You are not on the waitlist for this movie.";
			else if ( pos == 0 )
				msg="You are next in line to check out this movie.";
			else if ( pos == 1 )
				msg="There is 1 person ahead of you on the waitlist.";
			else
				msg="There are "+numToString(pos)+" people ahead of you on the waitlist.";
			alert(msg,BRyellow,blue,"");
		}
	}

} // end display full function

void movie::displayWaitlist()
{
	if ( waitlist.size() < 1 ) 
	{
		alert("This movie has no waitlist");
		return;
	}

	setStatus("Viewing waitlist for "+title);
	vector<string> customerList;
	queue<string> temp=waitlist;

	while ( !temp.empty() )
	{
		customerList.push_back(temp.front());
		temp.pop();
	};

	displayCustomerList(customerList,"Waitlist");

	setStatus("");

}

void movie::displayCheckoutList()
{
	setStatus("Viewing checkout list for "+title);
	displayCustomerList(copyStatus,"Checkout List");
	setStatus("");
}

void movie::displayCustomerList(vector<string> customerList, string header)
{

	char choice;
	enum choices
	{ NEXT_ITEM=80, PREV_ITEM=72, EXIT=27, PGDN=81, PGUP=73, ENTER=13 };
	const unsigned int ITEMS_PER_PAGE=SCREEN_HEIGHT-9;

	int row=5;
	int selectedItem=0;
	int topItem=0;
	setColor(BRyellow,BRred);
	setScreenPos(row,1);
	cout << left << setw(SCREEN_WIDTH-2) << title+" "+header;

	// each element of customerList is the driver's license of the customer
	// load and append name and phone #
	for ( unsigned int i=0; i < customerList.size(); i++ )
	{
		ifstream inFile;
		string filename="customers\\"+customerList[i];
		inFile.open(filename.c_str());
		customer tmpCustomer;
		if ( inFile )
		{
			tmpCustomer.load(inFile,filename);
			customerList[i].append(" "+tmpCustomer.getName()+" "+tmpCustomer.getPhone());
		}
		inFile.close();		
	}

	do {
		row=6;
		setScreenPos(row,1);

		int lastItem=topItem + ITEMS_PER_PAGE;
		for ( int i=topItem; i <= lastItem; i++ )
		{
			setScreenPos(row,1);	row++;
			if ( i < customerList.size() )
			{
				if ( i == selectedItem )
					setColor(BRyellow,BRblue);	// highlight selected item
				else
					setColor(black,BRblack);	// not selected, use normal color
			} else {
				setColor(black,black);			// blank line drawn black on black
			}
			
			if ( i < customerList.size() )
				cout << left << setw(SCREEN_WIDTH-2) << customerList[i];
			else
				for ( int j=0; j < SCREEN_WIDTH-2; j++ ) cout << " ";	// fill line with space to erase previous entry
		}

		choice=_getch();
		char letter=tolower(choice);

		if ( -32 == choice || 0 == choice ) 
			choice=_getch();

		// handle key presses
		if ( NEXT_ITEM == choice )
		{	// arrow down
			if ( selectedItem < customerList.size()-1 )
			{	
				selectedItem++;
				if ( selectedItem > lastItem ) topItem++;	// scroll down
			}
		} else if ( PREV_ITEM == choice ) 
		{	// arrow up
			if ( selectedItem > 0 )
			{
				selectedItem--;
				if ( selectedItem < topItem ) topItem--;	// scroll up
			}
		} else if ( PGDN == choice )
		{	// page down
			int tmp=topItem+ITEMS_PER_PAGE;
			if ( tmp >= customerList.size() )	tmp=customerList.size()-1;
			selectedItem=topItem=tmp;
		} else if ( PGUP == choice )
		{	// page up
			int tmp=topItem-ITEMS_PER_PAGE;
			if ( tmp < 0 ) tmp=0;
			selectedItem=topItem=tmp;
		} 

	} while ( choice != EXIT && customerList.size() > 0 );

	// clear list
	position topLeft;
	topLeft.row=5;
	topLeft.col=1;
	removeBorder(topLeft,SCREEN_WIDTH-4,SCREEN_HEIGHT-8);
}

int movie::getAvailableCopy()
{
	int tryCopy=0;
	bool found=false;

	do
	{
		if ( copyStatus[tryCopy].length() < 1 )
			found=true;
		else
			tryCopy++;
	} while ( tryCopy < copyStatus.size() && !found );

	if ( found ) 
		return tryCopy;
	else
		return -1;
}

int movie::checkout(customer* theCustomer, bool silent)
{
	// returns the copy number that was issued, may be ignored by some calling functions

	int copyNum=getAvailableCopy();

	if ( copyNum > -1 )
	{
		// record the customer's id in the copyStatus array
		copyStatus[copyNum]=theCustomer->getDriversLicense();

		// make sure the customer doesn't already have this movie checked out
		if ( !theCustomer->onCheckoutList(id) )
		{
			// tell the customer about the transaction
			// append the copy # to the id
			string tmpID=id;
			tmpID.append("-");
			tmpID.append(numToString(copyNum));

			theCustomer->checkoutMovie(tmpID);
			//rentals++;
			increaseRentals();

			if ( !silent ) alert("Okay, Checked out "+title, BRyellow, blue, "Success");
		} else
		{
			alert("You already have that movie checked out.");
			copyNum=-1;
		}
	} else if ( !silent ) {
		// no available copies
		alert("No available copies are currently in stock");

		// add to waitlist?
		menu ask("Put on the wait list?");
		ask.addChoice("Yes");
		ask.addChoice("No");
		ask.addChoice("How big is the wait list?");

		enum choices
		{	YES, NO, HOWBIG };

		int choice;
		do {
			choice=ask.getUserChoice(NO);
			if ( HOWBIG == choice ) 
				alert("There are currently "+numToString(waitlist.size())+" people waiting for this movie.", BRyellow,blue,"");
			else if ( YES == choice )
			{
				// add to wait list
				if ( getQueuePosition(theCustomer->getDriversLicense()) > -1 )
					alert("You are already on the waitlist for this movie.");
				else
				{
					waitlist.push(theCustomer->getDriversLicense());
					adjustWaitlistCount();
					alert("You have been added to the waitlist",BRyellow,blue,"Success");
				}
			}
		} while ( choice == HOWBIG );

	}
	return copyNum;
}

void movie::increaseRentals()
{
	rentals++;
	movie::setSortField(SORT_FIELD_RENTALS);
	forestPTR->trees[SORT_FIELD_RENTALS].reinsertNode(this);
}

void movie::adjustWaitlistCount()
{
	// the waitlist count has changed, this node needs to be reinserted into the waitlist tree
	movie::setSortField(SORT_FIELD_WAITLISTCOUNT);
	forestPTR->trees[SORT_FIELD_WAITLISTCOUNT].reinsertNode(this);
}

int movie::getQueuePosition(string DLN)
{
	// determines if a person with the given driver's license # is on a queue (the waitlist)
	// if so returns how far down the queue
	int qPos = -1;
	bool found=false;

	DLN=lcase(DLN);

	queue<string> temp=waitlist;
	while ( !temp.empty() && !found )
	{
		qPos++;

		string currentItem=temp.front();
		currentItem=lcase(currentItem);
		if ( currentItem == DLN )
		{
			found=true;
		}
		temp.pop();
	};

	if ( found )
		return qPos;
	else
		return -1;
}

string movie::checkin(int copyNum)
{
	// checks in the copy and returns the ID of the customer who had rented it
	string custID;
	if ( copyNum >= 0 && copyNum < copyStatus.size() )
	{
		custID=copyStatus[copyNum];
		copyStatus[copyNum]="";
		// handle waitlist
		if ( !waitlist.empty() ) 
		{
			serveWaitlist();
		}
	} else {
		alert("That copy number doesn't exist");
	}

	return custID;
}

void movie::serveWaitlist()
{
	// serves the next person on the waitlist
	assert (!waitlist.empty());

	string customerID=waitlist.front();
	waitlist.pop();
	adjustWaitlistCount();

	customer *theCustomer = new customer();

	string filename="customers\\";
	filename.append(customerID);
	ifstream inFile;
	inFile.open(filename.c_str());
	if ( inFile )
	{
		int copyNum;
		theCustomer->load(inFile,filename);
		copyNum = checkout(theCustomer, true);
		theCustomer->save();
		addNotification(theCustomer->getName(), theCustomer->getPhone(), theCustomer->getDriversLicense(), copyNum);
	} 

	delete theCustomer;
	inFile.close();
}

void movie::addNotification(string name, string phone, string dln, int copyNum)
{
	ofstream outFile;
	outFile.open(NOTIFY_FILE,ios::app);
	if ( outFile )
	{
		string note;
		note.append(makeDate(time(0),true));
		note.append("|"+name+"|"+phone+"|"+dln+"|");
		note.append(title+"|"+id+"|");
		note.append(numToString(copyNum));
		note.append("|0");

		outFile << note << endl;
	}
	outFile.close();
}

int movie::sortField;
forest* movie::forestPTR;