#include "List.h"
#include "Parser.h"

#include <assert.h>
#include <sstream>

using namespace std;

List::List(int lineNumber)
:_item(NULL),_next(NULL),_lineNumber(lineNumber)
{
}

List::List(List *list)
:_item(NULL),_next(NULL)
{
	if(list) {

		if(list->_item) {
			if(list->_item->atomic())
				_item = new Atom((Atom *)list->_item);
			else
				_item = new List((List *)list->_item);
		}

		//_next = new List(list->rest());

		List *listNext = list->_next;
		List *myNext = this;
		while(listNext != NULL) {
			myNext->_next = listNext->copy();
			myNext = myNext->_next;
			listNext = listNext->_next;
		}
	}
}

List *List::copy()
{
	List *newList = new List(_lineNumber);

	if(_item) {
		if(_item->atomic())
			newList->_item = new Atom((Atom *)_item);
		else
			newList->_item = new List((List *)_item);
	}

	return newList;
}

List::~List()
{

	// FIXME -- Make sure this code really works
	delete _item;

	List *current = _next;
	while(current != NULL) {
		List *next = current->_next;
		current->_next = NULL;
		delete current;
		current = next;
	}

	// recursive version exhausts the call stack on long lists
	/*
	delete _item;
	delete _next;
	*/
}

int List::getInt()
{
	cerr << _lineNumber << ": Attempt to get int from list object" << endl;
	return 0;
}

double List::getDouble()
{
	cerr << _lineNumber << ": Attempt to get double from list object" << endl;
	return 0;
}


//FIXME -- rewrite these functions to not use recursion
std::string List::getString()
{
    if(empty())
        return "";
    else {
        stringstream listString;
	listString << Parser::open << first()->getString();
        string restString = rest()->getRestString();
        if(restString != "")
            listString << " " << rest()->getRestString();
        listString << Parser::close;
	return listString.str();
    }
}

std::string List::getRestString()
{
    if(empty())
        return "";
    else {
        stringstream listString;
        listString << first()->getString();
        string restString = rest()->getRestString();
        if(restString != "")
            listString << " " << rest()->getRestString();
        return listString.str();
    }
}

std::string List::firstString()
{
	return first()->getString();
}

int List::firstInt()
{
	return first()->getInt();
}

double List::firstDouble()
{
	return first()->getDouble();
}

List *List::firstList()
{
	return first()->getList();
}

ListItem *List::first()
{
	return _item;
}

List *List::rest()
{
	return _next;
}

List *List::cons(ListItem *first)
{
	List *newList = new List();
	newList->_item = first;
	newList->_next = this;
	return newList;
}

List *List::snoc(ListItem *last)
{
	// recursive version exhausts the call stack on long lists
	/*
	if(empty()) {
		_item = last;
		_next = new List();
	}
	else
		_next->snoc(last);
	return this;
	*/

	List* current = this;

	while(!current->empty())
		current = current->rest();

	current->_item = last;
	current->_next = new List();

	return this;
}

void List::findAndReplace(std::string find, std::string replace)
{
	List * current = this;

	while(!current->empty()) {
		current->_item->findAndReplace(find,replace);
		current = current->rest();
	}
}

Atom::Atom(std::string aString,int lineNumber)
:_string(aString),_lineNumber(lineNumber)
{
}

Atom::Atom(int anInt, int lineNumber)
:_lineNumber(lineNumber)
{
	stringstream num;
	num << anInt;
	_string = num.str();
}

Atom::Atom(double aDouble, int lineNumber)
:_lineNumber(lineNumber)
{
	stringstream num;
	num << aDouble;
	_string = num.str();
}

Atom::Atom(Atom *atom)
:_string(atom->_string),_lineNumber(atom->_lineNumber)
{
}

List *Atom::getList()
{
	cerr << _lineNumber << ": Attempt to getList on atomic element: " << _string << endl;
	return NULL;
}

int Atom::getInt()
{
	return atoi(_string.c_str());
}

double Atom::getDouble()
{
	return atof(_string.c_str());
}

void Atom::findAndReplace(std::string find, std::string replace)
{
	if(_string == find)
		_string = replace;
}

std::string Atom::firstString()
{
	cerr << _lineNumber << ": Attempt to firstString on atomic element: " << _string << endl;
	return "";
}

List *Atom::firstList()
{
	cerr << _lineNumber << ": Attempt to firstList on atomic element: " << _string << endl;
	return NULL;
}