/*
 * 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.
 */

#ifndef __VAR_H
#define __VAR_H


extern Node* FreeVarNodes;
extern Node* FreeUndefNodes;

class Undef;

// Variable 
class Var : public Node {
private:
	std::string	name;
	Node*		varnode;

public:
	Var(std::string nm);
	 ~Var() {};

	std::string Name() { return name; }

	Node* Val();

	// lisp functions
	Node* Car() { return Val()->Car(); }
	Node* Cdr() { return Val()->Cdr(); }
	int   Eq(Node* nd) { return Val()->Eq(nd); }
	int   Nul() { return Val()->Nul(); }

	Node* Dup() {
		if (dupp == NULL) {
			extern Var* NewVar(std::string s);
			dupp = NewVar(name);
		}
		return dupp; 
	}
	void  DuppClr() {
		dupp = NULL;
		varnode->DuppClr();
	}
	Node* Insert(Node* nd) { return Val()->Insert(nd); }
	void  Del() { Val()->Del(); }
	Node* Find(Node* nd) { return Val()->Find(nd); }

	// print nodes
	void printsub(FILE* fd) {
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}

		fprintf(fd, "%s", name.c_str()); 
	}
	void printcdrsub(FILE* fd) {
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}
		fprintf(fd, " :"); printsub(fd); 
	}

	// get Undef node corresponding to Var node.
	Undef*	GetUndef();

	// If it is an undefined variable, set a value.
	// If it is an defined variable, return false.
	int	Set(Node* n);

	// set a value forcibly.
	int	SetForce(Node* n);

	// unset a value forcibly.
	void	UnsetForce();

	// GC
	void gcmark();

	void init(std::string nm);

};

extern Node* __UNDEF__;
extern Var* NewVar(std::string s);

// Undefined Var
class Undef : public Node {
private:
	Node*		value; // When a value is fixed, it is set.
			       // When it is undefined, set __UNDEF__ value, 
			       // and it is not Nil

	unsigned int	udefno;  // UNDEF Number
public:
	Undef();
	~Undef() {};

	Node* Val();

	// lisp functions
	Node* Car() { return value->Car(); }
	Node* Cdr() { return value->Cdr(); }
	int   Eq(Node* nd) { return value->Eq(nd); }
	int   Nul() { return value->Nul(); }

	Node* Dup() { return this; }	// Undef is not able to Dup
	void  DuppClr() {
		dupp = NULL;
		value->DuppClr();
	}
	Node* Insert(Node* nd) { syserr("Insert Node to Undefined Var"); return Nil; }
	void  Del() { syserr("Del Node of Undefined Var"); }
	Node* Find(Node* nd) { syserr("Find Node for Undefined Var"); return Nil; }

	// print nodes
	void printsub(FILE* fd) { 
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}

		if (value == __UNDEF__) {
			fprintf(fd, "Undef%u", udefno);
		} else {
			value->printsub(fd);
		}
	}
	void printcdrsub(FILE* fd) {
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}

		if (value == __UNDEF__) {
			fprintf(fd, " :");
			fprintf(fd, "Undef%u", udefno); 
		} else {
			Val()->printcdrsub(fd);
		}
	}

	int	Set(Node* n) {
		if (value->kind() == VAR) {
			((Var*)value)->Set(n);
			return 0;
		} else if (value->kind() != UNDEF) {
			value = n->Val();
			return 0;
		}
		
		Node* npsave;
		Node* np;
		npsave = value;
		np = ((Undef*)value)->value;		
		for ( ; ;np=((Undef*)value)->value) {
			if (np->kind() != UNDEF) {
				break;
			}
			npsave = np;
		}

		((Undef*)npsave)->value = n->Val();
		return 1;
	}

	// unset a value forcibly.
	void	UnsetForce();

	Node* GetUndefValue() { return value; }

	// GC
	void gcmark();

	void init();
};

/* Conversion of variable */
extern Var* GetVar(const char* s);
extern void ClearVar();

#endif // __VAR_H

