/* Lecture code 13.1
 *
 * Using auto_ptr to prevent resource leaks in a linked list class.
 */

#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>
#include <memory> // For auto_ptr
using namespace std;

/* A linked list cell. */
struct cellT
{
	int data;
	auto_ptr<cellT> next;

	/* Counter data to track the number of cellTs left over. */
	static int getInstances() { return numInstances; }
	cellT() { ++numInstances; }
	cellT(const cellT &other) { ++numInstances; }
	~cellT() { --numInstances; }
private:
	static int numInstances;
};

int cellT::numInstances = 0;

/* Reads in a single integer from a file and stores it in a dynamically-allocated
 * cellT struct.  If the stream fails when reading and is not failing from EOF,
 * throws a runtime_error exception.  If the stream hits the end, returns NULL
 * to indicate "end of list."
 */
cellT* GetNextCell(ifstream& input)
{
	/* If we're at the end of the file, hand back NULL. */
	if(input.eof())
		return NULL;

	int value;
	input >> value;

	/* If the stream failed, throw an exception. */
	if(input.fail())
		throw runtime_error("Failed to read an int!");

	/* Else, build a new cell and return it. */
	cellT* result = new cellT;
	result->data = value;
	return result;
}

/* Given a filename representing a file containing a list of integers,
 * reads in those integers into a linked list and returns it.  If the
 * file is malformed or nonexistent, throws a runtime_error exception.
 * The list is returned in reverse order.
 */
cellT* ReadIntegers(const string& filename)
{
	ifstream input(filename.c_str());
	if(!input.is_open())
		throw runtime_error("Couldn't open the file!");

	/* Read one element at a time. */
	auto_ptr<cellT> result;
	
	while(true)
	{
		cellT *newCell = GetNextCell(input);
		if(newCell == NULL)
			return result.release();
		
		/* Prepend to the list. */
		newCell->next.reset(result.get());
		result.release();
		result.reset(newCell);
	}
}

int main()
{
	try
	{
		/* Read in the integers.  If an error occurs, we jump to the
		 * exception handler.
		 */
		auto_ptr<cellT> list(ReadIntegers("numbers.txt"));

		/* Print the list. */
		for(cellT* current = list.get(); current != NULL; current = current->next.get())
			cout << current->data << endl;
	}
	catch(const runtime_error& rex)
	{
		cout << "Error: " << rex.what() << endl;
	}
	cout << "Number of cellTs left: " << cellT::getInstances() << endl;
	return 0;
}