/*
 * module program  H.Niwa copyright (C) 2009
 */

/*
 * 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 "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 "module.h"
#include "unify.h"

void PPmoduleHead(int tabs, Node* nd);
void PPmoduleBody(int tabs, Node* nd);
void PPmoduleBody(int tabs, Node* nd, int tabsflag);
void PPmodule(Node* module);
void PPmodule(int tabs, Node* module);


inline int ModuleCompare(Node* l1, Node* l2)
{
	int r;
	if ((l1->kind() == ATOM) && (l2->kind() == ATOM)) {
		std::string s1, s2;
		((Atom*)l1)->toString(s1);
		((Atom*)l2)->toString(s2);
		if (s1 == s2) {
			return 0;
		} else if (s1 > s2) {
			return 1;
		} else {
			return -1;
		}
	} else if ((l1->kind() == LIST) && (l2->kind() == LIST)) {
		r = ModuleCompare(l1->Car(), l2->Car());
		if (r != 0) {
			return r;
		}
		return ModuleCompare(l1->Cdr(), l2->Cdr());
	} else if ((l1->kind() == LIST) && (l2->kind() == ATOM)) {
		return -1;
	} else if ((l1->kind() == ATOM) && (l2->kind() == LIST)) {
		return 1;
	}
	return 0;
}


void ModuleSort(List* module)
{
	Node*	l1;
	Node*	l2;
	for (l1 = module->Car(); l1->Cdr() != Nil; l1=l1->Cdr()) {
		for (l2 = l1->Cdr(); l2 != Nil; l2=l2->Cdr()){
			if (ModuleCompare(l1->Car()->Car()->Car(), 
					  l2->Car()->Car()->Car()) > 0) {
				Node* tmp = l1->Car();
				((List*)l1)->SetCar(l2->Car());
				((List*)l2)->SetCar(tmp);
			}

		}
	}
}

void Assert(List* module, Node* nd)
{
	module->SetCar(Append(module->Car(),Cons(nd, Nil)));

//	ModuleSort(module);
}
	
void Asserta(List* module, Node* nd)
{
	module->SetCar(Cons(nd, module->Car()));

//	ModuleSort(module);
}


void PPmoduleOld(Node* module)
{
    Node *nd;
    Node *ndpred;
    for (nd = module->Car(); nd != Nil; nd = nd->Cdr()) {
	nd->Car()->Car()->print();
	if (nd->Car()->Cdr() == Nil) {
		printf(";\n");
	} else {
		printf("\n");
	}

	for (ndpred = nd->Car()->Cdr(); ndpred != Nil;
				     ndpred = ndpred->Cdr()) {
	    printf("\t");
	    ndpred->Car()->print();
	    if (ndpred->Cdr() != Nil) {
		    printf("\n");
	    } else {
		    printf(";\n");
	    }
	}
    }
}

static void ptabs(int n)
{
        for (int i=0; i < n; i++) {
    		printf("\t");
        }
}

static int checkmodule(Node* ndh)
{

	if (ndh->kind() != PRED) {
//printf("checkmodule trace 0\n");
		return 0;
	}
	if (ndh->Cdr()->Car()->kind() != LIST) {
//printf("checkmodule trace 1\n");
		return 0;
	}
	if (ndh->Cdr()->Car()->Car()->kind() != PRED) {
//printf("checkmodule trace 2\n");
		return 0;
	}
	if (ndh->Cdr()->Cdr()->Car()->kind() != LIST) {
//printf("checkmodule trace 3\n");
		return 0;
	}
	if (ndh->Cdr()->Cdr()->Car()->Car()->kind() != PRED) {
//printf("checkmodule trace 4\n");
		return 0;
	}

	return 1;

}

	
/* Prety Print Head module */
void PPmoduleHead(int tabs, Node* ndh)
{
	Node* n;
	
	ptabs(tabs);
	if (checkmodule(ndh)) {
		printf("<");
		ndh->Car()->print();
		printf("\n");

	    	PPmodule(tabs+1, MkList(ndh->Cdr()));

		ptabs(tabs);
	    	printf(">\n");
	} else {
		ndh->print();
	}

}

void PPmodulePred(int tabs, Node* ndpred, int tabsflag)
{
//PrintNode("PPmodulePred 0 ", ndpred);
	if (tabsflag) {
		ptabs(tabs);
	}
	if (ndpred->Car()->Eq(mka("obj")) ||
	    ndpred->Car()->Eq(mka("unify"))) {
		printf("::");
		ndpred->Cdr()->Car()->print();
		printf(" ");
		PPmodulePred(tabs, ndpred->Cdr()->Cdr()->Car(), 0);
	} else if (ndpred->Car()->Eq(mka("loop"))) {
		printf("{\n");
		ptabs(tabs);
		PPmoduleBody(tabs, ndpred->Cdr(), 0);
		ptabs(tabs);
		printf("}\n"); 
	} else if (ndpred->Car()->Eq(mka("alt"))) {
		printf("[\n");
		ptabs(tabs);
		PPmoduleBody(tabs, ndpred->Cdr(), 0);
		ptabs(tabs);
		printf("]\n"); 
	} else if (ndpred->Car()->Eq(mka("or"))) {
		PPmoduleBody(tabs, ndpred->Cdr()->Car(), 0);
		for (Node* np=ndpred->Cdr()->Cdr(); 
					np->kind() != ATOM; 
						np=np->Cdr()) {
			ptabs(tabs);
			printf("   |\n");
			PPmoduleBody(tabs, np->Car());
		}
	} else if (ndpred->Car()->Eq(mka("for"))) {
		PrintNode("<for ", ndpred->Cdr()->Car());
		PPmoduleBody(tabs+1, ndpred->Cdr()->Cdr());
		ptabs(tabs);
		printf(">\n");
	} else if ((ndpred->Car()->Eq(mka("foreach"))) ||
    	    		(ndpred->Car()->Eq(mka("map")))) {
		PrintNode("<foreach ", ndpred->Cdr()->Car());
		PPmoduleBody(tabs+1, ndpred->Cdr()->Cdr());
		ptabs(tabs);
		printf(">\n");
	} else {
		ndpred->print();
		printf("\n");
	}
}


void PPmoduleBody(int tabs, Node* nd)
{
	PPmoduleBody(tabs, nd, 1);
}

/* Prety Print Body module */
void PPmoduleBody(int tabs, Node* nd, int tabsflag)
{
	Node *ndpred;
//PrintNode("PPmoduleBody ", nd);

	if (nd->kind() == PRED) {
		PPmodulePred(tabs, nd, tabsflag);
		return;
	}
	for ( ; nd != Nil; nd = nd->Cdr()) {
		ndpred = nd->Car();
		if (ndpred == Nil) {
			continue;
		}
		PPmodulePred(tabs, ndpred, tabsflag);
		tabsflag = 1;
	}
}

/* Prety Print module for test */
void PPmodule(Node* module)
{
	PPmodule(0, module);
}

void PPmodule(int tabs, Node* module)
{
    Node *nd;
    
    for (nd = module->Car(); nd->kind() != ATOM; nd = nd->Cdr()) {

	PPmoduleHead(tabs, nd->Car()->Car());
	
	if (nd->Car()->Cdr() == Nil) {
		printf("\n"); ptabs(tabs+1); printf(";\n");
		continue;
	} else {
		printf("\n");
	}

	PPmoduleBody(tabs+1, nd->Car()->Cdr());
	ptabs(tabs); printf("\t;\n");
    }
}

