// KeyList.cpp
// CSI 2131 Asst 2b by Naim R. El-Far (naim@discover.uottawa.ca)
// CSI 2131 - Winter 2006 - Prof. L.M. Moura

// File:	KeyList.cpp
// Desc:	Class KeyList definitions

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include "KeyList.h"
using namespace std;

KeyList::KeyList() {
	keyListFileName = "";
	keyListFileStream;
	numberOfKeysInList = 0;
}

KeyList::KeyList(string fileName) {
	keyListFileName = fileName;
	keyListFileStream.open(keyListFileName.c_str(), ios::out);
	keyListFileStream.close();
	keyListFileStream.open(keyListFileName.c_str(), ios::in | ios::out | ios::binary);
	if (!keyListFileStream) {
		cerr << endl
			 << "---------------------------------------------------" << endl
			 << "Error! Specified key list file could not be opened!" << endl
			 << "---------------------------------------------------" << endl
			 << endl;
		exit(1);
	}
	else
		cout << "Key list file " << fileName << " opened successfully." << endl
			 << endl;
	numberOfKeysInList = 0;
}

KeyList::~KeyList() {
	keyListFileStream.close();
}

void KeyList::Insert(string studentNumber, long int listNext) {
	//Write student number in text mode
	keyListFileStream << studentNumber;

	//Write list next value in binary mode after converting from decimal to binary number
	keyListFileStream.write((char const*)(&listNext), 4);
}

void KeyList::WriteByRRN(long int RRN, string studentNumber, long int listNext) {
	long int entryOffset = keyListFileStream.tellg();

	keyListFileStream.clear();
	keyListFileStream.seekp(RRN * KEY_LIST_RECORD_SIZE);

	Insert(studentNumber, listNext);

	keyListFileStream.clear();
	keyListFileStream.seekg(entryOffset);
}

void KeyList::UpdateListNext(long int givenRRN, long int newListNextValue) {
	string		originalStudentNumber = "";
	long int	originalListNext = -2;
	ReadByRRN(givenRRN, originalStudentNumber, originalListNext);
	WriteByRRN(givenRRN, originalStudentNumber, newListNextValue);
}

void KeyList::ReadByRRN(long int RRN, string& studentNumber, long int& listNext) { //Given an RRN and a string and long int in which to store the read values, retrieve the student number and list next values and store them in the given variables
	long int entryOffset = keyListFileStream.tellg();
	
	keyListFileStream.clear();
	keyListFileStream.seekg(RRN * KEY_LIST_RECORD_SIZE);

	char studentNumberChar[20];
	keyListFileStream.read(studentNumberChar, 7);
	keyListFileStream.read((char*)(&listNext), 4);

	studentNumber = "";
	for (int i = 0 ; i < 7 ; i++)
		studentNumber = studentNumber + studentNumberChar[i];

	keyListFileStream.clear();
	keyListFileStream.seekg(entryOffset);
}

void KeyList::IncrementNumberOfKeys() {
	numberOfKeysInList++;
}

long int KeyList::InsertIntoInvertedList(long int listHead, string studentNumberToInsert) { //Called only if there are duplicate secondary keys (i.e. last names)
	bool done = false;
	long int lookingAtRRN = listHead; //Indicates the current RRN
	long int listNext = -2; //Indicates the next RRN
	long int listPrevious = -2; //Indicates the previous RRN
	long int newListHead = -2;
	int intGivenStudentNumber = atoi(studentNumberToInsert.c_str());
	
	while (!done) {
		string retrievedStudentNumber = "";
		ReadByRRN(lookingAtRRN, retrievedStudentNumber, listNext);
		int intRetrievedStudentNumber = atoi(retrievedStudentNumber.c_str());
	
		if (intGivenStudentNumber == intRetrievedStudentNumber) {
			cerr << endl
				<< "---------------------------------" << endl
				<< "Error! Duplicate student numbers!" << endl
				<< "---------------------------------" << endl
				<< endl;
			exit(1);
		}
		else if (intGivenStudentNumber > intRetrievedStudentNumber) {
			if (listNext == -1) {
				newListHead = listHead; //List head doesn't change
				UpdateListNext(lookingAtRRN, numberOfKeysInList);
				WriteByRRN(numberOfKeysInList, studentNumberToInsert, -1); //Add to the end of the list
				done = true;
			}
			else {
				listPrevious = lookingAtRRN;
				lookingAtRRN = listNext;
			}
		}
		else { //if (intGivenStudentNumber < intRetrievedStudentNumber)
			WriteByRRN(numberOfKeysInList, studentNumberToInsert, lookingAtRRN);  //We are appending to the list, so we're inserting at the last position + 1
			if (lookingAtRRN == listHead) //If we're inserting at the beginning of a list, the list head changes
				newListHead = numberOfKeysInList;
			else {
				newListHead = listHead; //List head doesn't change
				UpdateListNext(listPrevious, numberOfKeysInList); //Update the previous record to point to this record
			}
			done = true;
		}
	}
	numberOfKeysInList++;
	return newListHead;
}

void KeyList::PrintList(ostream& str) {
	str << setw(7) << setiosflags(ios::left) << "Number" << '\t' << setw(10) << setiosflags(ios::left) << "List Next" << endl
		<< setw(7) << setiosflags(ios::left) << "------" << '\t' << setw(10) << setiosflags(ios::left) << "---------" << endl;

	string studentNumber = "";
	long int listNext = -2;
	for (int i = 0 ; i < numberOfKeysInList ; i++) {
		ReadByRRN(i, studentNumber, listNext);
		str << setw(7) << setiosflags(ios::left) << studentNumber << '\t' << setw(10) << setiosflags(ios::left) << listNext << endl;
	}
	str << "--END OF KEY LIST--" << endl;
}