/*
 * Var node program copyright (C) 2009 - 2013 H.Niwa 
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include <string>
#include <complex>

#include "syserr.h"

#include "bin_node.h"
#include "var.h"

unsigned int	undefvar = 0;

// __UNDEF__ object
Atom  __UNDEF__area("__UNDEF__");
Node* __UNDEF__ = &__UNDEF__area;

Node* FreeVarNodes = NULL;
Node* FreeUndefNodes = NULL;

Var* NewVar(std::string s)
{
	if (FreeVarNodes == NULL) {
		return new Var(s);
	} else {
		Node* nd = FreeVarNodes;
		FreeVarNodes = nd->nextnode;
		nd->nextnode = AllNodes->nextnode;
		AllNodes->nextnode = nd;
		((Var*)nd)->init(s);
		return (Var*)nd;
	}
}

static Undef* NewUndef()
{
	if (FreeUndefNodes == NULL) {
		return new Undef();
	} else {
		Node* nd = FreeUndefNodes;
		FreeUndefNodes = nd->nextnode;
		nd->nextnode = AllNodes->nextnode;
		AllNodes->nextnode = nd;
		((Undef*)nd)->init();
		return (Undef*)nd;
	}
}



Var::Var(std::string s)
{
	k = VAR;
	varnode = NewUndef();
	
	name = s;
}

Node* Var::Val()
{
	Node* np;

	for (np = varnode; ; np = ((Var*)np)->varnode) {
		if (np->kind() != VAR) {
			return np->Val();
		}
	}
}


Undef* Var::GetUndef()
{
	Node* np;
	for (np = varnode; ; np = ((Var*)np)->varnode) {
		if (np->kind() == UNDEF) {
			return (Undef*)np;
		} else if (np->kind() != VAR) {
			syserr("VAR ... not UNDEF");
			exit(-1);
		}
		// if node is a VAR, continue for loop
	}
}


int Var::Set(Node* n)
{
	Node* nval = n;
	Node* np;

	// if specified value is Var Node, set the value of Var.
	if (n->kind() == VAR) {
		nval = ((Var*)n)->Val();
		if (nval->kind() == UNDEF) {
			nval = n;
		}
	}

	// If it is an undefined variable, set a value.
	// If it already have a value, return false.
	for (np = varnode; ; np = ((Var*)np)->varnode) {
		if (np->kind() == UNDEF) {
			((Undef*)np)->Set(nval);
			return 1;
		} else if (np->kind() != VAR) {
			syserr("VAR ... not UNDEF");
			return 0;
		}
		// if node is a VAR, continue for loop
	}
}

int Var::SetForce(Node* n)
{
	Node* np;
	Node* nval = n->Val();

	if (n->kind() == VAR) {
		nval = ((Var*)n)->Val();
		if (nval->kind() == UNDEF) {
			nval = n;
		}
	}

	for (np = varnode; ; np = ((Var*)np)->varnode) {
		if (np->kind() == UNDEF) {
			int r = ((Undef*)np)->Set(nval);
//printf("SetForce "); Val()->print(); printf("\n");
			return r;
		} else if (np->kind() != VAR) {
			syserr("VAR ... not UNDEF");
			return 0;
		}
	}

}

void Var::UnsetForce()
{
	varnode = NewUndef();
	
}

void Var::gcmark()
{
	Node* np;
	extern void GCmark(Node*);

	if (ref) return;

	ref++;

	for (np = varnode; ; np = ((Var*)np)->varnode) {
		GCmark(np);
		if (np->kind() == UNDEF) {
			break;
		} else if (np->kind() != VAR) {
			break;
		}
	}
}

inline void Var::init(std::string s)
{
	//k = VAR;
	varnode = NewUndef();
	
	name = s;
}


Undef::Undef() 
{
	k = UNDEF; 
	udefno = undefvar++; 
	value = __UNDEF__; 
}

Node* Undef::Val() 
{ 
	extern void CheckTime();
	CheckTime();
	
	if (value != __UNDEF__) {
		if (value == this) {
			syserr("value of undefined var is looped ");
			return Nil;
		}
		CheckTime();

		return value->Val(); 
	} else {
		return this;
	}
}

void Undef::gcmark()
{
	extern void GCmark(Node*);
	
	if (ref) return;

	ref++;

	GCmark(value);
}

void Undef::UnsetForce()
{
	value = __UNDEF__; 
}

inline void Undef::init() 
{
	//k = UNDEF; 
	udefno = undefvar++; 
	value = __UNDEF__; 
}


/* Conversion of variable */

Node*	varlist = Nil;

void RegisterVar(Var* v)
{
	varlist = Cons(v, varlist);
}

Var* SearchVar(const char* s)
{
	Node* p;
	for (p = varlist; p != Nil; p=p->Cdr()) {
		if (((Var*)(p->Car()))->Name() == s) {
			return ((Var*)p->Car());
		}
	}
	return NULL;
}

Var* GetVar(const char* s)
{
	if (strncmp(s, "_", 1) != 0) {
		Var* v = SearchVar(s);
		if (v != NULL) {
			return v;
		}
	}
	Var* r = NewVar(s);
	RegisterVar(r);
	return r;
}

void ClearVar()
{
	varlist = Nil;
}

