#include <../../nrnconf.h>
#include <stdio.h>
#include <InterViews/resource.h>
#include "nrnoc2iv.h"
#include "classreg.h"

extern "C" {
#include "membfunc.h"
#include "parse.h"
}

extern "C" {
extern Prop* prop_alloc(Prop**, int, Node*);
extern void single_prop_free(Prop*);
extern Symlist* hoc_built_in_symlist;
extern Symlist* hoc_top_level_symlist;
}

//----------------------------------------------------
/* static */ class NrnPropertyImpl {
	friend class NrnProperty;
	NrnPropertyImpl(Prop*);
	~NrnPropertyImpl();
	
	Prop* p_;
	int iterator_;
	Symbol* sym_;
	bool del_;
};
NrnPropertyImpl::NrnPropertyImpl(Prop* p) {
	p_ = p;
	iterator_ = -1;
	sym_ = memb_func[p_->type].sym;
	del_ = false;
}

NrnPropertyImpl::~NrnPropertyImpl() {
	if (del_ && p_) {
//printf("NrnProperty freed prop\n");
		single_prop_free(p_);
	}
}

//---------------------------------------------------
/* static */ class SectionListImpl {
	friend class SectionList;
	Object* ob_;
	struct hoc_Item* itr_;
	struct hoc_Item* list_;
};

//----------------------------------------------------
#if 0
/* static */ class NrnSectionImpl {
	friend NrnSection
	NrnSectionImpl(Section*);
	~NrnSectionImpl();
	
	Section* sec_;
};
NrnSectionImpl::NrnSectionImpl(Section* sec) {
	sec_ = sec;
	section_ref(sec);
}
NrnSectionImpl::~NrnSectionImpl() {
	section_unref(sec_);
}
#endif
//----------------------------------------------------
NrnProperty::NrnProperty(Prop* p) {
	npi_ = new NrnPropertyImpl(p);
}

NrnProperty::NrnProperty(const char* name) {
	Symbol* sym = hoc_table_lookup(name, hoc_built_in_symlist);
	if (!sym) {
		sym = hoc_table_lookup(name, hoc_top_level_symlist);
	}
	if(sym) {
		if (sym->type == MECHANISM) {
			/*EMPTY*/
		}else if (sym->type == TEMPLATE && sym->u.ctemplate->is_point_){
			sym = hoc_table_lookup(name, sym->u.ctemplate->symtable);

		}else{
			sym = 0;
		}
	}
	if (sym) {
		Prop* p, *p0 = 0, *p1;
//printf("prop_alloc %s %p type=%d\n", sym->name, sym, sym->subtype);
		// need to do this with no args
		hoc_push_frame(sym, 0);
		p = prop_alloc(&p0, sym->subtype, NULL);
		hoc_pop_frame();
		for (; p0 != p; p0 = p1) {
			p1 = p0->next;
			single_prop_free(p0);
		}
		npi_ = new NrnPropertyImpl(p);
		npi_->del_ = true;
	}else{
		npi_ = NULL;
		hoc_execerror(name, "is not a Mechanism or Point Process");
	}
}

NrnProperty::~NrnProperty(){
	delete npi_;
}

bool NrnProperty::deleteable() {
	return npi_->del_;
}

const char* NrnProperty::name() const {
	return npi_->sym_->name;
}

bool NrnProperty::is_point() const {
	return memb_func[npi_->p_->type].is_point;
}

int NrnProperty::type() const {
	return npi_->p_->type;
}

Prop* NrnProperty::prop() const {
	return npi_->p_;
}

Symbol*  NrnProperty::first_var() {
	npi_->iterator_ = -1;
	return next_var();	
}

bool NrnProperty::more_var() {
	if (npi_->iterator_ >= npi_->sym_->s_varn) {
		return false;
	}else{
		return true;
	}
}

Symbol* NrnProperty::next_var() {
	++npi_->iterator_;
	if (more_var()) {
		return npi_->sym_->u.ppsym[npi_->iterator_];
	}else{
		return 0;
	}
}

Symbol* NrnProperty::var(int i) {
	return npi_->sym_->u.ppsym[i];
}

int NrnProperty::var_type(Symbol* sym) const{
	return nrn_vartype(sym);
}

bool NrnProperty::assign(Prop* src, Prop* dest, int vartype) {
	int n;
	assert(vartype != NRNPOINTER);
	if (src && dest && src != dest && src->type == dest->type) {
		if (src->ob) {
			Symbol* msym = memb_func[src->type].sym;
			int i, j, jmax, cnt = msym->s_varn;
			for (i=0; i < cnt; ++i) {
				Symbol* sym = msym->u.ppsym[i];
				if (vartype == 0 || nrn_vartype(sym) == vartype) {
					jmax = hoc_total_array_data(sym, 0);
					n = sym->u.rng.index;
					double* x, *y;				
					y = dest->ob->u.dataspace[n].pval;
					x = src->ob->u.dataspace[n].pval;
					for (j=0; j < jmax; ++j) {
						y[j] = x[j];
					}
				}
			}
		}else{
		    if (vartype == 0) {
				n = src->param_size;
				for (int i=0; i < n; ++i) {
					dest->param[i] = src->param[i];
				}
		    }else{
			Symbol* msym = memb_func[src->type].sym;
			int i, j, jmax, cnt = msym->s_varn;
			for (i=0; i < cnt; ++i) {
				Symbol* sym = msym->u.ppsym[i];
				if (nrn_vartype(sym) == vartype) {
					jmax = hoc_total_array_data(sym, 0);
					n = sym->u.rng.index;
					for (j=0; j < jmax; ++j) {
						dest->param[n+j] = src->param[n+j];
					}
				}
			}
		    }
		}
		return true;	
	}else{
		return false;
	}
}
Symbol* NrnProperty::find(const char* name) {
	Symbol* sym;
	int i, cnt;
	cnt = npi_->sym_->s_varn;
	for (i=0; i < cnt; ++i) {
		sym = npi_->sym_->u.ppsym[i];
		if (strcmp(sym->name, name) == 0) {
			return sym;
		}
	}
	return 0;
}
int NrnProperty::prop_index(const Symbol* s)const {
	assert(s);
	if (s->type != RANGEVAR) {
		hoc_execerror(s->name , "not a range variable");
	}
	return s->u.rng.index;
}

double* NrnProperty::prop_pval(const Symbol* s, int index)const {
	if (npi_->p_->ob) {
		return npi_->p_->ob->u.dataspace[prop_index(s)].pval + index;
	}else{
		if (s->subtype == NRNPOINTER) {
			return npi_->p_->dparam[prop_index(s) + index].pval;
		}else{
			return npi_->p_->param + prop_index(s) + index;
		}
	}
}

//--------------------------------------------------
SectionList::SectionList(Object* ob) {
	sli_ = new SectionListImpl();
	check_obj_type(ob, "SectionList");
	sli_->ob_ = ob;
	++ob->refcount;
	sli_->list_ = (struct hoc_Item*) ob->u.this_pointer;
	sli_->itr_ = sli_->list_;
}
SectionList::~SectionList() {
	hoc_dec_refcount(&sli_->ob_);
	delete sli_;
}
Section* SectionList::begin() {
	sli_->itr_ = sli_->list_->next;
	return next();
}
Section* SectionList::next() {
	Section* sec;
	if (sli_->itr_ == sli_->list_) {
		sec = NULL;
	}else{
		sec = hocSEC(sli_->itr_);
		sli_->itr_ = sli_->itr_->next;
	}
	return sec;
}

#if 0
//---------------------------------------------------
NrnSection::NrnSection(Section* sec) {
	npi_ = new NrnSectionImpl(sec);
}
NrnSection::~NrnSection() {
	delete npi_;
}
void NrnSection::section(Section* sec) {
	section_ref(sec);
	section_unref(npi_->sec_);
	npi_->sec_ = sec;
}
Section* NrnSection::section() {
	return npi_->sec_;
}

Node* NrnSection::node(int inode) { return sec_->pnode[inode]; }

bool NrnSection::is_mechanism(type) {
	return nrn_mechanism(type, node(0)) != (Prop*)0
}

double* NrnSection::var_pointer(const char* var) {
	nrn_pushsec(nsi_->sec_);
	double* pval = hoc_val_pointer(var);
	nrn_popsec();
	return pval;
}
#endif
