/*
 * garbage collector program copyright (C) 2009 - 2012 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>
#include <complex>

#include "syserr.h"

#include "bin_node.h"
#include "gc.h"
#include "var.h"
#include "pred.h"
#include "context.h"
#include "builtin.h"
#include "sysmodule.h"
#include "unify.h"
#include "context.h"
#include "gc.h"
#include "module.h"

int memspeed_diff = 0;

void GCmark(Node* nd)
{
	
	nd->gcmark();
}

void freeListNode()
{
	if (FreeListNodes == NULL) return;
	
	Node* nd, * ndold;

	for (nd = FreeListNodes; nd != NULL; ) {
		ndold = nd;
		nd = nd->nextnode;
		delete ndold;
	}
	FreeListNodes = NULL;
}

void freeAtomNode()
{
	if (FreeAtomNodes == NULL) return;
	
	Node* nd, * ndold;

	for (nd = FreeAtomNodes; nd != NULL; ) {
		ndold = nd;
		nd = nd->nextnode;
		delete ndold;
	}
	FreeAtomNodes = NULL;
}

void freePredNode()
{
	if (FreePredNodes == NULL) return;
	
	Node* nd, * ndold;

	for (nd = FreePredNodes; nd != NULL; ) {
		ndold = nd;
		nd = nd->nextnode;
		delete ndold;
	}
	FreePredNodes = NULL;
}

void freeVarNode()
{
	if (FreeVarNodes == NULL) return;
	
	Node* nd, * ndold;

	for (nd = FreeVarNodes; nd != NULL; ) {
		ndold = nd;
		nd = nd->nextnode;
		delete ndold;
	}
	FreeVarNodes = NULL;
}

void freeUndefNode()
{
	if (FreeUndefNodes == NULL) return;
	
	Node* nd, * ndold;

	for (nd = FreeUndefNodes; nd != NULL; ) {
		ndold = nd;
		nd = nd->nextnode;
		delete ndold;
	}
	FreeUndefNodes = NULL;
}


void GC()
{
	GC(Nil);
}

static int gcloop0 = 0;

void GC(Node* bd)
{
	Node*	nd;
	Node*	ndprev;

// return;
#if 1
	if (memspeed_diff > 10000) {
		memspeed_diff = 0;

	} else if (gcloop0 < 1000) {
		gcloop0++;

		if ((FreeListNodes != NULL) 
		 || (FreeAtomNodes != NULL)
		 || (FreePredNodes != NULL) 
		 || (FreeVarNodes != NULL) 
		 || (FreeUndefNodes != NULL) ) {
			return;
		}
	} else {
		gcloop0 = 0;
	}
#endif

	freeListNode();
	freeAtomNode();
	freePredNode();
	freeVarNode();
	freeUndefNode();

	extern Atom AllNodesBody;

	// clear mark
	for (nd = AllNodes->nextnode; nd != NULL; nd = nd->nextnode) {
		nd->ref = 0;
	}

	AllNodesBody.ref = 1;
	AllNodes->ref = 1;
	
	GCmark(AllNodes);
	GCmark(True);
	GCmark(Nil);
	GCmark(__UNDEF__);
	GCmark(ErrGoal);
	GCmark(dlibpathnode);

	GCmark(bd);

	GCmark(Module);

	Context* cxp;
	for (cxp = CXNode; cxp != NULL; cxp = cxp->Next()) {
		GCmark(cxp->inherit);
		GCmark(cxp->env_stack);
		GCmark(cxp->env);
		GCmark(cxp->misc_stack);
		GCmark(cxp->modulename);
		GCmark(cxp->ode);
		GCmark(cxp->integral);
	}

	AllNodesBody.ref = 1;
	AllNodes->ref = 1;

	// free node
	for (ndprev = AllNodes, nd = AllNodes->nextnode; 
			nd != NULL; ) {
				
		if (nd->ref) {
			ndprev = ndprev->nextnode;
			nd = nd->nextnode;
		} else {
			Node* ndold = nd;
			nd = ndprev->nextnode = nd->nextnode;
			switch (ndold->kind()) {
			case LIST:
				ndold->nextnode = FreeListNodes;
				FreeListNodes = ndold;
				break;
			case ATOM:
				ndold->nextnode = FreeAtomNodes;
				FreeAtomNodes = ndold;
				break;
			case PRED:
				ndold->nextnode = FreePredNodes;
				FreePredNodes = ndold;
				break;
			case VAR:
				ndold->nextnode = FreeVarNodes;
				FreeVarNodes = ndold;
				break;
			case UNDEF:
				ndold->nextnode = FreeUndefNodes;
				FreeUndefNodes = ndold;
				break;
			default:
				delete ndold;
				break;
			}
		}
	}
}

int CountNode()
{
	Node*	nd;
	int	n;

	n = 0;
	for (nd = AllNodes; nd != NULL; nd = nd->nextnode) {
		n++;
	}

	return n;
}


