// joycub V2 - an interpeter for the programming language Joy
// (c) 2004-2006 Sergey Zubkov 
static char RCSID[] ="$Id: reference.cc,v 1.11 2006/02/27 18:44:01 cubbi Exp $";

// used by print_banner() and J2_dataobj::debug_print()
#include <iostream>
#include "globals.h"
#include "pool.h"
#include "reference.h"
#include "object.h"
#include "set.h"
#include "string.h"
#include "op.h"
#include "float.h"
#include "bool.h"
#include "char.h"
#include "exceptions.h"

using namespace std;

// global object counters (for memory debugging)
int ref_type_count = 0;

JC_objref::JC_objref(JC_object* a) {
	ref_type_count++;
	content_type = a->get_type();
//cout << "  inserting " << a->key() << " : ";
	if(content_type==SET || content_type==LIST || content_type==STRING) {
		agg= dynamic_cast<JC_list*>(a);
	} else {
		it = POOL[content_type].find(a->key());
		if(it != POOL[content_type].end()) {
			POOL[content_type][a->key()]->inc_ref();
			delete a;
//cout << " refcount increased to " << (*it).second->get_ref() << endl;
		} else {
			a->inc_ref();
			POOL[content_type][a->key()]=a;
			it = POOL[content_type].find(a->key());
//cout << " new element: refcount set to " << (*it).second->get_ref() << endl;
		}
	}
}

// Copy constructor: make a new reference to an existing object
JC_objref::JC_objref(const JC_objref & j) {
	ref_type_count++;
	content_type=j.content_type;
	if(content_type == SET || content_type == LIST || content_type==STRING) {
//cout << " copy constructor for " << j.agg->key() << endl;
		switch(content_type) {
		case SET:
			   	agg = new JC_set( *dynamic_cast<JC_set*>(j.agg) );
				break;
		case LIST:
			   	agg = new JC_list( *dynamic_cast<JC_list*>(j.agg) );
				break;
		case STRING:
			   	agg = new JC_string( *dynamic_cast<JC_string*>(j.agg) );
				break;
		default: break;
		}
	} else {
//cout << " copy constructor for " << (*j.it).second->key() << endl;
		it=j.it;
		(*it).second->inc_ref();
	}
}

JC_objref::~JC_objref() {
	ref_type_count--;
	if(content_type == SET || content_type == LIST || content_type==STRING) {
//cout << " reference to " << agg->key() << " deleting: " << endl;
		delete agg;
	} else {
//cout << " reference to " << (*it).second->to_string() << " deleting: " << endl;
		(*it).second->dec_ref();
//cout << " new ref count is " << (*it).second->get_ref() << endl;
		if(0 == (*it).second->get_ref()) {
			delete (*it).second;
			POOL[content_type].erase(it);
		}
	}
}

JC_objref* JC_objref::dup() const {
//cout << "::dup called for " << to_string() << endl;
	if(content_type == SET || content_type == LIST || content_type==STRING) {
		return new JC_objref(agg); 
	} else {
		return new JC_objref((*it).second);
	}
}

string JC_objref::to_string() const {
	return is_agg() ? agg->to_string() : (*it).second->to_string();
}
string JC_objref::to_output_string() const {
	return is_agg() ? agg->to_output_string() : (*it).second->to_output_string();
}
bool JC_objref::is_agg() const {
	return content_type == SET || content_type == LIST || content_type==STRING;
}

// return the object's key
string JC_objref::key() {
	return is_agg() ? agg->key() : (*it).second->key();
}

const obj_type JC_objref::get_type() const {
	return is_agg() ? agg->get_type() : (*it).second->get_type();
}

int JC_objref::to_int() const {
	if(content_type == INT) {
		return (dynamic_cast<JC_int*>((*it).second))->to_int();
	} else if (content_type == CHAR) {
		return (dynamic_cast<JC_char*>((*it).second))->to_int();
	} else {
		throw runtime_error(to_string()+" is not an integer");
	}
}
double JC_objref::to_double() const {
	if(content_type == INT) {
		return (dynamic_cast<JC_int*>((*it).second))->to_int();
	} else if (content_type == FLOAT) {
		return (dynamic_cast<JC_float*>((*it).second))->to_double();
	} else {
		throw runtime_error( to_string()+" is not a float or int");
	}
}
FILE* JC_objref::to_file() const {
	if(content_type == _FILE_) {
		return (dynamic_cast<JC_file*>((*it).second))->to_file();
	} else {
		throw runtime_error( to_string()+" is not a file");
	}
}
bool JC_objref::to_bool() const {
	if(content_type == BOOL) {
		return (dynamic_cast<JC_bool*>((*it).second))->to_bool();
	} else {
		throw runtime_error( to_string()+" is not a bool");
	}
}
int JC_objref::get_opnum() const {
	if(content_type == OPERATOR) {
		return (dynamic_cast<JC_op*>((*it).second))->get_num();
	} else {
		throw runtime_error( to_string()+" is not an operator");
	}
}
JC_list& JC_objref::to_agg() const {
	if(content_type == LIST || content_type==SET || content_type==STRING) {
		return *agg;
	} else {
		throw runtime_error( to_string()+" is not aggregate type");
	}
}
