/*
 * Binary node program copyright (C) 2009 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 __BIN_NODE_H
#define __BIN_NODE_H

// kind of node
enum Knode { UNDEF, LIST, ATOM, VAR, PRED };


class Node;


extern Node* Cons(Node* nd1, Node* nd2);
extern Node* GetLeftNode(Node* goals, Node* &goalscdr, int& level);
extern Node* EraseLevelNode(Node* goals, int level);
extern Node* Append(Node* nd1, Node* nd2);

extern Node* AllNodes;

extern unsigned int PrCount;
#define	PRCOUNTLIMIT	(PrCount+100)

class Node {
private:

public:
	unsigned int	ref;	// counter for GC

	Knode		k;	// kind of Node

	Node*		dupp;	// A pointer to prevent repetition of Dup.
				// It points at a new node copied in Dup.
				// use a Dup() function of unify.c.
				

	Node*		nextnode;	// link of all nodes

	unsigned int   prcount;

	Node() { nextnode = AllNodes; AllNodes = this; prcount = 0; }
	virtual ~Node() {};

	Knode	kind() { return k; }
	
	// function return a value that extended a variable.
	virtual Node* Val() = 0;

	// lisp functions
	virtual Node* Car() = 0;
	virtual Node* Cdr() = 0;
	virtual Node* Cons(Node* nd);
	virtual int   Eq(Node* nd) = 0;
	virtual int   Nul() = 0;

	virtual Node* Dup() = 0;
	virtual	void  DuppClr() = 0;		// clear dupp
	virtual Node* Insert(Node* nd) = 0;
	virtual void  Del() = 0;
	virtual Node* Find(Node* nd) = 0;

	// print nodes
	virtual void print() {
		print(stdout);
	}
	
	virtual void print(FILE* fd) {
		PrCount++;
		printsub(fd);
	}
	
	virtual void printcdr() {
		printcdr(stdout);
	}

	virtual void printcdr(FILE* fd) {
		PrCount++;
		printcdrsub(fd);
	}
	
	virtual void printsub(FILE* fd){}
	virtual void printcdrsub(FILE* fd){}

	unsigned int	Ref() { return ref; }

	// GC
	virtual void gcmark() = 0;
	
};

// True, Nil
extern Node* True;
extern Node* Nil;

// Atom is strings
class Atom : public Node {
private:
	std::string*	str;
	double		floatvalue;
	int		floatvalue_flag;

public:
	Atom(char* s) { k = ATOM; str = new std::string(s); floatvalue_flag=0;}
	Atom(long long n) 
		{
			char* m = (char*)malloc(256);
			if (m == NULL) {
				syserr("can't get memory of Atom");
			}
			snprintf(m, 256-1, "%lld", n); 
			k = ATOM; 
			str = new std::string(m); 
			free(m);
			floatvalue_flag = 0;
		}
	Atom(double n)
		{
			char* m = (char*)malloc(256);
			if (m == NULL) {
				syserr("can't get memory of Atom");
			}
			snprintf(m, 256-1, "%g", n); 
			k = ATOM; 
			str = new std::string(m); 
			free(m);
			floatvalue_flag = 1;
			floatvalue = n;
		}
			
	
	~Atom() { delete str; }

	Node*	Val() { return this; };

	int	toString(std::string& s) {s = *str; };
	long long toLLInt(long long& n);

	int	toFloat(double& f);

	// lisp functions
	Node* Car() { return this; }
	Node* Cdr() { return this; }
	// cdr of Nil is Nil .

	int   Eq(Node* nd);
	int   EqStr(std::string s);
	int   Nul() { return (this == Nil); }

	Node* Dup() { return this; }
	void  DuppClr() { dupp = NULL; }

	Node* Insert(Node* nd) {
		if (this == Nil) {
			return nd;
		}
		syserr("Insert Node to Atom"); 
		return Nil; 
	}
	void  Del() { syserr("Del Node of Atom"); }
	Node* Find(Node* nd) { syserr("Find Node for Atom"); return Nil; }

	
	// print nodes
	void printatom(FILE* fd, char* s);

	void printsub(FILE* fd) {
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}
		printatom(fd, (char*)str->c_str());
	}

	void printcdrsub(FILE* fd) {
		if (this != Nil) {
			if (prcount >= PRCOUNTLIMIT) {
				fprintf(fd, " ...");
				return;
			} else {
				if (prcount < PrCount) {
					prcount = PrCount;
				} else {
					prcount++;
				}
			}
			fprintf(fd, " :");
			printatom(fd, (char*)str->c_str());
		}
	}

	// GC
	void gcmark() { ref++; }
};


// binary list
class List : public Node {
private:
	Node* car;
	Node* cdr;

public:
	List() { k = LIST; car = Nil; cdr = Nil; }
	List(Node* n1, Node* n2) { k = LIST; car = n1; cdr = n2; }
	~List() {}

	Node* Val() { return Car()->Val()->Cons(Cdr()->Val()); }

	// lisp functions
	Node* Car() { return car; }
	Node* Cdr() { return cdr; }
	int   Eq(Node* nd);
	int   Nul() { return 0; }

	Node* Dup() {
		if (dupp == NULL) 
			dupp = Car()->Dup()->Cons(Cdr()->Dup());
		return dupp; 
	}
	void  DuppClr() {
		dupp = NULL; 
		Car()->DuppClr();
		Cdr()->DuppClr(); 
	}
	Node* Insert(Node* nd);
	void  Del();
	Node* Find(Node* nd);

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

	void printcdrsub(FILE* fd) {
		if (prcount >= PRCOUNTLIMIT) {
			fprintf(fd, " ...");
			return;
		} else {
			if (prcount < PrCount) {
				prcount = PrCount;
			} else {
				prcount++;
			}
		}
		if (this != Nil) {
			fprintf(fd, " ");
			Car()->printsub(fd);
			Cdr()->printcdrsub(fd);
		}
	}

	void Set(Node* n1, Node* n2) { car = n1; cdr = n2; }
	void SetCar(Node* n) { car = n; }
	void SetCdr(Node* n) { cdr = n; }

	// GC
	void gcmark() { 
		if (ref) return;
		ref++; car->gcmark(); cdr->gcmark(); 
	}
};

extern Node* MkList(Node* n1);
extern Node* MkList(Node* n1, Node* n2);
extern Node* MkList(Node* n1, Node* n2, Node* n3);
extern Node* MkList(Node* n1, Node* n2, Node* n3, Node* n4);
extern Node* MkList(Node* n1, Node* n2, Node* n3, Node* n4, Node* n5);
extern Node* MkList(Node* n1, Node* n2, Node* n3, Node* n4, Node* n5, Node* n6);
extern Node* MkList(Node* n1, Node* n2, Node* n3, Node* n4, Node* n5, Node* n6, Node* n7);


inline void PrintNode(char* s, Node* nd)
{
	printf("%s", s);
	nd->print();
	printf("\n");
}

inline void PrintNode(char* s1, Node* nd, char* s2)
{
	printf("%s", s1);
	nd->print();
	printf("%s\n", s2);
}

#endif // __BIN_NODE_H
