/*
 * check source validity program copyright (C) 2011 - 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 <unistd.h>
#include <string.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 "unify.h"
#include "builtin.h"
#include "sysmodule.h"
#include "module.h"
#include "help.h"
#include "checkreserved.h"
#include "checksrc.h"


// Definition of CHKSRC predicate


#define BUILTIN	"_BUILTIN"
#define SYSMOD	"sys"

Node* getdefinedpred(Node* src, Node* objname)
{
	Node* nd = Nil;
	std::string spred = "", sobj = "";

	int i;		
	
	int rsvlen = NumOfReserved();
	
	for (i = 0; i < rsvlen; i++) {
		spred =  ReservedWords[i];
		sobj = BUILTIN;
		nd = Append(nd, 
			Cons(Cons(mka(spred), 
				Cons(mka(sobj), mka(0LL))), Nil));
	}

	int syslen = NumOfSysmod();

	for (i = 0; i < syslen; i++) {
		spred =  SysWords[i];
		sobj = SYSMOD;
		nd = Append(nd, 
			Cons(Cons(mka(spred), 
				Cons(mka(sobj), mka(0LL))), Nil));
	}


	for (; src->kind() != ATOM; src = src->Cdr()) {
		if (src->Car()->Car()->Car()->kind() == PRED) {
			((Atom*)src->Car()->Car()->Car()->Car())->toString(sobj);
			Node* n = getdefinedpred(src->Car()->Car()->Cdr(), 
						mka(sobj));
			nd = Append(nd, n);
		} else if (src->Car()->Car()->Car()->kind() == ATOM) {
			((Atom*)src->Car()->Car()->Car())->toString(spred);
			long long len = ListLength(src->Car()->Car());

			nd = Append(nd, 
				Cons(Cons(mka(spred), 
					Cons(objname, mka(len))), Nil));
		}				
	}
	return nd;
}

int findpred(Node* nobj, Node* npred, int arglen1, Node* definedpred)
{
	Node* 	n = Nil;
	std::string sobj1="", spred1="",sobj2="", spred2="";
	long long arglen2;
		

	if (nobj->Car()->kind() == ATOM) {
		((Atom*)nobj->Car())->toString(sobj1);
	} else {
		return 1;
	}

	if (npred->Car()->kind() == ATOM) {
		((Atom*)npred->Car())->toString(spred1);

		if ((spred1 == "let") || (spred1 == "letf") || 
						(spred1 == "letc")) {
			return 1;
		} else if (spred1[0] == '#') {
			return 1;
		}
	} else {
		return 1;
	}

	std::string BUILTINSTR = BUILTIN, SYSMODSTR = SYSMOD;
	for (n = definedpred; n->kind() != ATOM; n = n->Cdr()) {

		if ((n->Car()->Car()->kind() != ATOM) ||
		    (n->Car()->Cdr()->Car()->kind() != ATOM) ||
		    (n->Car()->Cdr()->Cdr()->kind() != ATOM)) {
		    	return 1;
		}
		
		((Atom*)n->Car()->Car())->toString(spred2);
		((Atom*)n->Car()->Cdr()->Car())->toString(sobj2);
		((Atom*)n->Car()->Cdr()->Cdr())->toInt(arglen2);

//PrintNode(n->Car());

		if (spred1 == spred2) {
			if ((sobj1 == sobj2) && (arglen1 == arglen2)) {
			    	return 1;
			} else if ((sobj2 == "") && (arglen1 == arglen2)) {
				return 1;
			} else if (sobj1 == "__UNDEF__") {
				return 1;
			} else if (sobj2 == BUILTINSTR) {
				return 1;
			} else if ((sobj2 == SYSMODSTR) ||
				   (sobj2 == "compiler") || 
				   (sobj2 == "matrix") || 
				   (sobj2 == "curses") || 
				   (sobj2 == "generator") || 
				   (sobj2 == "link") ||
				   (sobj2 == "list")) {
				if (sobj1 == sobj2) {
					return 1;
				}
			}
		}
	}

	if (sobj1 != "") {
		printf("::%s ", sobj1.c_str());
	}

	PrintNode(npred);

	return 0;
}

void checksrcpred1line(Node* pred, Node* defined, Node* objname)
{
	std::string spred = "";

	if (pred == Nil) {
		return;
	} else if (pred->kind() == PRED) {
		if (pred->Car()->kind() == ATOM) {
			((Atom*)pred->Car())->toString(spred);
			if (spred == "or") {
				checksrcpred1line(pred->Cdr(), defined, 
								objname);
			} else {
				if (spred == "obj") {
					Node* npred = pred->Cdr()->Cdr();
					Node* nobj = pred->Cdr()->Car();


					checksrcpred1line(npred->Cdr(),
							defined, nobj);

					findpred(nobj, npred->Car(), 
					   ListLength(npred->Car()), defined);

				} else {

					checksrcpred1line(pred->Cdr(), 
							defined, objname);

					findpred(objname, pred, 
						ListLength(pred), defined);
				}
			}
		} else if (pred->Car()->kind() == PRED) {
			Node* npred = pred->Cdr();
			Node* nobj = pred->Car()->Car();

			checksrcpred1line(npred, defined, nobj);
		}
	} else if (pred->kind() == LIST) {
		checksrcpred1line(pred->Car(), defined, objname);
		checksrcpred1line(pred->Cdr(), defined, objname);
	}

}

void checksrcpred(Node* defined, Node* src, Node* objname)
{
	Node* n = Nil;
	
	for (; src->kind() != ATOM; src = src->Cdr()) {
		checksrcpred1line(src->Car(), defined, objname);
	}
}

int CheckSrc(Context* cx, Node* goalscar, List* module)
{
	Node* g = goalscar->Cdr()->Val();
	int	arglen = ListLength(g);

	if (arglen != 0) {
		syserr("usage : <CHECKSRC>\n");
		return 0;
	}

	Node* src = Module->Car();

	Node* definedpred = getdefinedpred(src, mka(""));

	printf("\n*** The following predicate might be undefined *** \n");
	
	checksrcpred(definedpred, src, mka(""));

	printf("\n--\n");
	
	return 1;
}

