#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <conio.h>

using namespace std;
using std::ifstream;
using std::ostream;
using std::string;
using std::setprecision;

//Bringing in a ClientData header file
#include "C:\L\z\SelfEd\BankRelated\BankClient\ClientData.h"

//Enumerating menu options that the user will be using
enum UserChoice { PRINT = 1, NEW, VIEW, UPDATE, DELETE, RENEW, END, totalChoices };

//Array of strings to hold menu options for user to visually read from
string strChoices[totalChoices];

//Defining some constants
#define AccNumWidth		7
#define CashWidth		8
#define MaxAccounts		10

void	Init			();
int		EnterChoice		();
void	CreateTextFile	(fstream &);
void	NewRecord		(fstream &);
void	UpdateRecord	(fstream &);
void	DeleteRecord	(fstream &);
void	RenewAccounts	(fstream &);
void	ViewCurrent		(fstream &);
void	OutputLine		(ostream &, const ClientData &);
int		GetAccNum		(const char *Message);

int main()
{
	int		UserChoice = -1;

	Init();

	fstream InOutData("C:\\L\\z\\SelfEd\\BankRelated\\BankClient\\Credit.dat", ios::in | ios::out);

	if(!InOutData)
	{
		cout << "Unable to open Client.dat\n";
		system("pause");
	}
	else
	{
		//EnterChoice prints menu, and returns a value that is used as test value in this while-loop
		while((UserChoice = EnterChoice()) != END)
		{
			//Reset location each time to ensure index is correct
			InOutData.seekg(0);

			switch(UserChoice)
			{
			case PRINT:		CreateTextFile	(InOutData);		break;
			case NEW:		NewRecord		(InOutData);		break;
			case UPDATE:	UpdateRecord	(InOutData);		break;
			case DELETE:	DeleteRecord	(InOutData);		break;
			case RENEW:		RenewAccounts	(InOutData);		break;
			case VIEW:		ViewCurrent		(InOutData);		break;
			case END:		cout << "Quiting...\n";				break;
			default: cout << "Input did not match choices.\n";	break;
			}
		}
		InOutData.clear();
	}
	InOutData.close();
	return 0;
}
void Init()
{
	strChoices[PRINT]	= "Print all accounts to text file";
	strChoices[UPDATE]	= "Update an account";
	strChoices[NEW]		= "Create an account";
	strChoices[DELETE]	= "Delete an account";
	strChoices[RENEW]	= "Renew all account";
	strChoices[VIEW]	= "View accounts";
	strChoices[END]		= "Quit Program";
}
int EnterChoice()
{
	int UserInput = -1;
	string tmp = "";

	do
	{
		system("cls");
		cout << "Enter Menu option...\n";
		for(int i = PRINT; i < totalChoices; i++)
			cout << right << setw(2) << i << ") " << strChoices[i] << "\n";
		cout << "\n";
		cin >> tmp;
		UserInput = atoi(tmp.c_str());

		if(UserInput < PRINT || UserInput >= totalChoices)
		{
			cout << "The value you entered did not compute...\n"
				 << "Please enter a value of allowed range (" << PRINT << " to " << (totalChoices - 1) << ")\n\n";

			system("pause");
		}
	}while(UserInput < PRINT || UserInput >= totalChoices);

	return UserInput;
}
void CreateTextFile(fstream &Data)
{
	ClientData	TmpClient1;
	ofstream	Output("PrintedAccounts.txt", ios::out);

	if(!Output) cout << "Unable to create PrintedAccounts.txt\n";
	else
	{
		Output << left	<< setw(AccNumWidth + 1)	<< "Account\t\t"
			   << left  << setw(NameSize)			<< "L Name"
			   << left  << setw(NameSize)			<< "F Name"
			   << right	<< setw(CashWidth)			<< "Balance"
			   << "\n";
		
		for(int i = 0; i < MaxAccounts; i++)
		{
			Data.seekg((i) * sizeof(ClientData));
			Data.read(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));

			if(TmpClient1.GetAcc() > -1)
			{
				OutputLine(Output, TmpClient1);
				Output << "\n";
			}
		}
	}

	//Close the file
	Output.close();
	
	cout << "Completed output...\n";
	system("pause");
}
void ViewCurrent(fstream &Data)
{
	ClientData	TmpClient1;
	
	system("cls");
	
	cout << left	<< setw(AccNumWidth + 1)	<< "Account\t\t"
		 << left	<< setw(NameSize)			<< "L Name"
		 << left	<< setw(NameSize)			<< "F Name"
		 << right	<< setw(CashWidth)			<< "Balance"
		 << "\n\n";

	for(int i = 0; i < MaxAccounts; i++)
	{
		Data.seekg((i) * sizeof(ClientData));
		Data.read(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));
        
		if(TmpClient1.GetAcc() > -1)
		{
			OutputLine(cout, TmpClient1);
			cout << "\n";
		}
	}

	//Add a line to clearn up output
	cout << "\n";
	system("pause");
}
void NewRecord(fstream &InsertTo)
{
	int			AccNum;
	char		LName[NameSize]	= {0};
	char		FName[NameSize]	= {0};
	double		Balance			= 0.0;

	ClientData	TmpClient1;

	AccNum = -1;

	do
	{
		AccNum = GetAccNum("Enter new account number");
		if(AccNum > 0)
		{
			//We move the file index into position
			InsertTo.seekg((AccNum - 1) * sizeof(ClientData));
			
			//We copy the data by size to our temp Client record from the data file
			InsertTo.read(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));
			
			//We test the temp Client's aacount number to see if it's occupied already
			if(TmpClient1.GetAcc() < 0)
			{
				cout << "Enter Last Name, First Name, Balance:\n";
				cin >> LName >> FName >> Balance;
				
				TmpClient1.SetData(AccNum, LName, FName, Balance);
				
				//We move back to the beginning of where the record is located
				InsertTo.seekp((AccNum - 1) * sizeof(ClientData));
				//We write the amount of data back to the file
				InsertTo.write(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));

				cout << "Account Number: " << AccNum << " has been created.\n\n";
				OutputLine(cout, TmpClient1);
			}
			else cout << "Account Number: " << AccNum << " is already occupied.\n";
			cout << "\n\n";
		}
		else
		{
			cout << "\nThe account number must be greater than zero\n";
			system("pause");
		}
	}while(AccNum < 1);
	system("pause");
}
void UpdateRecord(fstream &Update)
{
	int			AccNum	= GetAccNum("Enter account number to update");
	double		Trans	= 0.0;
	ClientData	TmpClient1;

	//We move to the location in the records
	Update.seekg((AccNum - 1) * sizeof(ClientData));

	//We read the data according to the size of the record
	Update.read(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));

	if(TmpClient1.GetAcc() > -1)
	{
		system("cls");
		OutputLine(cout, TmpClient1);
		cout << "\nEnter charge (+) or payment (-) (note, negative values will take from account): ";
		cin >> Trans;

		TmpClient1.SetBal(TmpClient1.GetBal() + Trans);

		//We move back to into position...
		Update.seekp((AccNum - 1) * sizeof(ClientData));
		//We overwrite the data...
		Update.write(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));

		cout << "\n\nRecord updated...\n\n";

		//Return to start of file
		Update.seekg(0);
	}
	else cout << "Account " << AccNum << " has no information.\n";

	system("pause");
}
void DeleteRecord(fstream &DeleteFrom)
{
	int AccNum = GetAccNum("Enter Account Number to Delete");
	ClientData TmpClient1; 
	ClientData TmpClient2;
	
	TmpClient2.Reset();
	
	//We move to the position of the index
	DeleteFrom.seekg((AccNum - 1) * sizeof(ClientData));

	//We read from that point
    DeleteFrom.read(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));
	
	cout << "Account Number: " << AccNum;

	if(TmpClient1.GetAcc() > -1)
	{
		//Move back into position...
		DeleteFrom.seekp((AccNum - 1) * sizeof(ClientData));
		//Overwrite the location with empty data...
		DeleteFrom.write(reinterpret_cast<char *>(&TmpClient2), sizeof(ClientData));

		cout << " deleted.\n";
	}
	else cout << " is empty.\n";
	cout << "\n\n";
	system("pause");
}
void RenewAccounts(fstream &ClearThese)
{
	ClientData TmpClient1;

	for(int i = 0; i < MaxAccounts; i++)
	{
		ClearThese.seekp((i) * sizeof(ClientData));
		ClearThese.write(reinterpret_cast<char *>(&TmpClient1), sizeof(ClientData));
	}
}
void OutputLine(ostream &myOut, const ClientData &Data)
{
	myOut << right	<< setw(AccNumWidth)	<< Data.GetAcc() << "\t\t"
		  << left	<< setw(NameSize)		<< Data.GetLName()
		  << left	<< setw(NameSize)		<< Data.GetFName()
		  << right	<< setw(CashWidth) << setprecision(2) << fixed	<< Data.GetBal();
}
int GetAccNum(const char *Message)
{
	int AccNum = -1;
	do
	{
		system("cls");
		cout << Message << " (1 - " << MaxAccounts << "): ";
		cin >> AccNum;
	}while(AccNum < 1 || AccNum > MaxAccounts);

	return AccNum;
}