/*
 * lib include module 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.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.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 "unify.h"
#include "sysmodule.h"
#include "matrix.h"
#include "opcall.h"
#include "lib_include.h"

#define MAXPATHLEN 4096

extern FILE* MksTemp(char* templ);

extern const char* liblist;
extern const char* libsys;
extern const char* libcurses;
extern const char* libcompiler;
extern const char* libmatrix;
extern const char* libgenerator;
extern const char* liblink;


FILE* lib_include(const char* s, char* &fname)
{
	std::string slib = s;

	if (slib == "list") {
		return savelibtmpfile(liblist, fname);
	} else if (slib == "compiler") {
		return savelibtmpfile(libcompiler, fname);
	} else if (slib == "matrix") {
		return savelibtmpfile(libmatrix, fname);
#if HAVE_LIBNCURSES && !defined(__MINGW32__)
	} else if (slib == "curses") {
		return savelibtmpfile(libcurses, fname);
#endif
	} else if (slib == "generator") {
		return savelibtmpfile(libgenerator, fname);
	} else if (slib == "link") {
		return savelibtmpfile(liblink, fname);
	}

	return NULL;
}

FILE* savelibtmpfile(const char* s, char* &fname)
{
	static char tmpfilename[MAXPATHLEN];
	strncpy(tmpfilename, tmppath, MAXPATHLEN);
	strcat(tmpfilename, "/descXXXXXX");
	FILE* fd = MksTemp(tmpfilename);
	if (fd == NULL) {
		syserr("tmpfile : cannot open tmp file \n");
		return 0;
	}
	
	for (; *s != 0; s++) {
		fputc(*s, fd);
	}
	
	fclose(fd);

	fd = fopen(tmpfilename, "rb");

	fname = tmpfilename;
	
	return fd;
}



/* ---------- list module ---------------------- */
const char* liblist 
	=
"<append #X () #X >;"
"<append (#A : #Z) (#A : #X)  #Y>"
	"<append #Z #X #Y >;"
"<reverse () ()>;"
"<reverse #r (#x: #l) >	"
	"<reverse #r1 #l >"
	"<append #r #r1 (#x) >;"
"<member #x (#x : #rest)>;"
"<member #x (#y : #rest)>"
	"<member #x #rest>;"
"<last #x (#x)>;"
"<last #x (_:#list)>"
	"<last #x #list>;"
"<flatten () ()>;"
"<flatten #list (#x:#l)>"
	"::sys <isList #x>"
	"<flatten #list1 #x>"
	"<flatten #list2 #l>"
	"<append #list #list1 #list2>;"
"<flatten #list (#x:#l) >"
	"<flatten #list2 #l>"
	"<append #list (#x) #list2>;"
"<difference () () _ >;"
"<difference #diff (#x:#set1) #set2 >"
	"<member #x #set2>"
	"<difference #diff #set1 #set2>;"
"<difference (#x : #diff) (#x:#set1) #set2>"
	"<difference #diff #set1 #set2>;"
"<equal_sets #set1 #set2>"
	"<difference () #set1 #set2>"
	"<difference () #set2 #set1>;"
"<intersect () () _ >;"
"<intersect (#x:#int) (#x:#set1) #set2>"
	"<member #x #set2>"
	"<intersect #int #set1 #set2>;"
"<intersect #int (_:#set1) #set2>"
	"<intersect #int #set1 #set2>;"
"<union #out () #out>;"
"<union #out (#x : #in1) #in2>"
	"<member #x #in2>"
	"<union #out #in1 #in2>;"
"<union (#x : #out) (#x : #in1) #in2>"
	"<union #out #in1 #in2>;"
"<subset () #set>;"
"<subset (#x : #subset) #set>"
	"<member #x #set>"
	"<subset #subset #set>;"
"<join () () ()>;"
"<join (#x #y : #out) (#x : #in1) (#y : #in2)>"
	"<join #out #in1 #in2>;"
"<occurs 0 _ ()>;"
"<occurs #n #e (#e : #list)>"
	"<occurs #m #e #list>"
	"<let #n = #m+1>;"
"<occurs #n #e (_ : #list)>"
	"<occurs #n #e #list>;"
	;


const char* libsys =
"<args:#x><opcall 1:#x>;"
"<DLIBPATH:#x><opcall 2:#x>;"
"<cutall:#x><opcall 3:#x>;"
"<mkpred:#x><opcall 4:#x>;"
"<writenl:#x><opcall 5:#x>;"
"<writeln:#x><opcall 6:#x>;"
"<print:#x><opcall 7:#x>;"
"<write:#x><opcall 8:#x>;"
"<isNil:#x><opcall 9:#x>;"
"<isAtom:#x><opcall 10:#x>;"
"<isList:#x><opcall 11:#x>;"
"<isPred:#x><opcall 12:#x>;"
"<isVar:#x><opcall 13:#x>;"
"<isUndefVar:#x><opcall 14:#x>;"
"<isFloat:#x><opcall 15:#x>;"
"<isInteger:#x><opcall 16:#x>;"
"<isInf:#x><opcall 17:#x>;"
"<isNan:#x><opcall 18:#x>;"
"<finte:#x><opcall 19:#x>;"
"<isTrue:#x><opcall 20:#x>;"
"<isFalse:#x><opcall 21:#x>;"
"<isUnknown:#x><opcall 22:#x>;"
"<max:#x><opcall 23:#x>;"
"<min:#x><opcall 24:#x>;"
"<maxf:#x><opcall 25:#x>;"
"<minf:#x><opcall 26:#x>;"
"<sum:#x><opcall 27:#x>;"
"<sumf:#x><opcall 28:#x>;"
"<avg:#x><opcall 29:#x>;"
"<avgf:#x><opcall 30:#x>;"
"<regex:#x><opcall 31:#x>;"
"<sub:#x><opcall 32:#x>;"
"<gsub:#x><opcall 33:#x>;"
"<split:#x><opcall 34:#x>;"
"<toupper:#x><opcall 35:#x>;"
"<tolower:#x><opcall 36:#x>;"
"<length:#x><opcall 37:#x>;"
"<random:#x><opcall 38:#x>;"
"<real:#x><opcall 39:#x>;"
"<image:#x><opcall 40:#x>;"
"<arg:#x><opcall 41:#x>;"
"<norm:#x><opcall 42:#x>;"
"<conj:#x><opcall 43:#x>;"
"<polar:#x><opcall 44:#x>;"
"<sin:#x><opcall 45:#x>;"
"<cos:#x><opcall 46:#x>;"
"<tan:#x><opcall 47:#x>;"
"<asin:#x><opcall 48:#x>;"
"<acos:#x><opcall 49:#x>;"
"<atan:#x><opcall 50:#x>;"
"<atan2:#x><opcall 51:#x>;"
"<sinh:#x><opcall 52:#x>;"
"<cosh:#x><opcall 53:#x>;"
"<tanh:#x><opcall 54:#x>;"
"<asinh:#x><opcall 55:#x>;"
"<acosh:#x><opcall 56:#x>;"
"<atanh:#x><opcall 57:#x>;"
"<log:#x><opcall 58:#x>;"
"<log10:#x><opcall 59:#x>;"
"<exp:#x><opcall 60:#x>;"
"<exp2:#x><opcall 61:#x>;"
"<exp10:#x><opcall 62:#x>;"
"<pow:#x><opcall 63:#x>;"
"<sqrt:#x><opcall 64:#x>;"
"<abs:#x><opcall 65:#x>;"
"<PI:#x><opcall 66:#x>;"
"<e:#x><opcall 67:#x>;"
"<int:#x><opcall 68:#x>;"
"<floor:#x><opcall 69:#x>;"
"<ceil:#x><opcall 70:#x>;"
"<trunc:#x><opcall 71:#x>;"
"<car:#x><opcall 72:#x>;"
"<cdr:#x><opcall 73:#x>;"
"<caar:#x><opcall 74:#x>;"
"<cadr:#x><opcall 75:#x>;"
"<cdar:#x><opcall 76:#x>;"
"<cddr:#x><opcall 77:#x>;"
"<caaar:#x><opcall 78:#x>;"
"<caadr:#x><opcall 79:#x>;"
"<cadar:#x><opcall 80:#x>;"
"<caddr:#x><opcall 81:#x>;"
"<cdaar:#x><opcall 82:#x>;"
"<cdadr:#x><opcall 83:#x>;"
"<cddar:#x><opcall 84:#x>;"
"<cdddr:#x><opcall 85:#x>;"
"<caaaar:#x><opcall 86:#x>;"
"<caaadr:#x><opcall 87:#x>;"
"<caadar:#x><opcall 88:#x>;"
"<caaddr:#x><opcall 89:#x>;"
"<cadaar:#x><opcall 90:#x>;"
"<cadadr:#x><opcall 91:#x>;"
"<caddar:#x><opcall 92:#x>;"
"<cadddr:#x><opcall 93:#x>;"
"<cdaaar:#x><opcall 94:#x>;"
"<cdaadr:#x><opcall 95:#x>;"
"<cdadar:#x><opcall 96:#x>;"
"<cdaddr:#x><opcall 97:#x>;"
"<cddaar:#x><opcall 98:#x>;"
"<cddadr:#x><opcall 99:#x>;"
"<cdddar:#x><opcall 100:#x>;"
"<cddddr:#x><opcall 101:#x>;"
"<cons:#x><opcall 102:#x>;"
"<nth:#x><opcall 103:#x>;"
"<code:#x><opcall 104:#x>;"
"<char:#x><opcall 105:#x>;"
"<byte:#x><opcall 106:#x>;"
"<asciichar:#x><opcall 107:#x>;"
"<utf8char:#x><opcall 108:#x>;"
"<eucchar:#x><opcall 109:#x>;"
"<sjischar:#x><opcall 110:#x>;"
"<concat:#x><opcall 111:#x>;"
"<concatcode:#x><opcall 112:#x>;"
"<leftstr:#x><opcall 113:#x>;"
"<rightstr:#x><opcall 114:#x>;"
"<substr:#x><opcall 115:#x>;"
"<insertstr:#x><opcall 116:#x>;"
"<bitand:#x><opcall 117:#x>;"
"<bitor:#x><opcall 118:#x>;"
"<bitxor:#x><opcall 119:#x>;"
"<bitnot:#x><opcall 120:#x>;"
"<shiftl:#x><opcall 121:#x>;"
"<shiftr:#x><opcall 122:#x>;"
"<eq:#x><opcall 123:#x>;"
"<noteq:#x><opcall 124:#x>;"
"<is:#x><opcall 125:#x>;"
"<getc:#x><opcall 126:#x>;"
"<putc:#x><opcall 127:#x>;"
"<getline:#x><opcall 128:#x>;"
"<syntax:#x><opcall 129:#x>;"
"<tmpfile:#x><opcall 130:#x>;"
"<line:#x><opcall 131:#x>;"
"<openr:#x><opcall 132:#x>;"
"<openw:#x><opcall 133:#x>;"
"<openwp:#x><opcall 134:#x>;"
"<gettime:#x><opcall 135:#x>;"
"<time:#x><opcall 136:#x>;"
"<date:#x><opcall 137:#x>;"
"<sleep:#x><opcall 138:#x>;"
"<usleep:#x><opcall 139:#x>;"
"<pause:#x><opcall 140:#x>;"
"<basename:#x><opcall 141:#x>;"
"<dirname:#x><opcall 142:#x>;"
"<suffix:#x><opcall 143:#x>;"
"<clear:#x><opcall 144:#x>;"
"<uname:#x><opcall 145:#x>;"
"<countnode:#x><opcall 146:#x>;"
"<gc:#x><opcall 147:#x>;"
	;

const char* libcurses =
"<start:#x><opcall 501:#x>;"
"<mvprintw:#x><opcall 502:#x>;"
"<printw:#x><opcall 503:#x>;"
"<refresh:#x><opcall 504:#x>;"
"<move:#x><opcall 505:#x>;"
"<cbreak:#x><opcall 506:#x>;"
"<nocbreak:#x><opcall 507:#x>;"
"<raw:#x><opcall 508:#x>;"
"<noraw:#x><opcall 509:#x>;"
"<echo:#x><opcall 510:#x>;"
"<noecho:#x><opcall 511:#x>;"
"<erase:#x><opcall 512:#x>;"
"<clear:#x><opcall 513:#x>;"
"<getch:#x><opcall 514:#x>;"
"<ungetch:#x><opcall 515:#x>;"
"<getstr:#x><opcall 516:#x>;"
"<inch:#x><opcall 517:#x>;"
"<instr:#x><opcall 518:#x>;"
"<isendwin:#x><opcall 519:#x>;"
"<beep:#x><opcall 520:#x>;"
"<flash:#x><opcall 521:#x>;"
"<hline:#x><opcall 522:#x>;"
"<vline:#x><opcall 523:#x>;"
"<keypad:#x><opcall 524:#x>;"
"<scrollok:#x><opcall 525:#x>;"
"<scrl:#x><opcall 526:#x>;"
"<LINES:#x><opcall 527:#x>;"
"<COLS:#x><opcall 528:#x>;"
"<delch:#x><opcall 529:#x>;"
"<mvdelch:#x><opcall 530:#x>;"
"<clrtobot:#x><opcall 531:#x>;"
"<clrtoeol:#x><opcall 532:#x>;"
"<border:#x><opcall 533:#x>;"
	;

const char* libcompiler =
"<CheckReserved:#x><opcall 601:#x>;"
"<GetVar:#x><opcall 602:#x>;"
"<AddVar:#x><opcall 603:#x>;"
"<CheckVar:#x><opcall 604:#x>;"
"<NewFunc:#x><opcall 605:#x>;"
"<EndFunc:#x><opcall 606:#x>;"
	;

const char* libmatrix =
"<zero:#x><opcall 701:#x>;"
"<unit:#x><opcall 702:#x>;"
"<i:#x><opcall 702:#x>;"
"<set:#x><opcall 703:#x>;"
"<display:#x><opcall 704:#x>;"
"<add:#x><opcall 705:#x>;"
"<sub:#x><opcall 706:#x>;"
"<mul:#x><opcall 707:#x>;"
"<mulscalar:#x><opcall 708:#x>;"
"<divscalar:#x><opcall 709:#x>;"
"<transpose:#x><opcall 710:#x>;"
"<t:#x><opcall 710:#x>;"
"<equal:#x><opcall 711:#x>;"
"<notequal:#x><opcall 712:#x>;"
"<dimentions:#x><opcall 713:#x>;"
"<getrow:#x><opcall 714:#x>;"
"<getcolumn:#x><opcall 715:#x>;"
"<swaprow:#x><opcall 716:#x>;"
"<swapcolumn:#x><opcall 717:#x>;"
"<rangerow:#x><opcall 718:#x>;"
"<rangecolumn:#x><opcall 719:#x>;"
"<range:#x><opcall 720:#x>;"
"<ismatrix:#x><opcall 721:#x>;"
"<isvector:#x><opcall 722:#x>;"
"<issquare:#x><opcall 723:#x>;"
"<isnull:#x><opcall 724:#x>;"
"<isdiagonal:#x><opcall 725:#x>;"
"<issymmetric:#x><opcall 726:#x>;"
"<isregular:#x><opcall 727:#x>;"
"<issingular:#x><opcall 728:#x>;"
"<det:#x><opcall 729:#x>;"
"<inv:#x><opcall 730:#x>;"
"<random:#x><opcall 731:#x>;"
"<getval:#x><opcall 732:#x>;"
"<setval:#x><opcall 733:#x>;"
"<solve:#x><opcall 734:#x>;"
"<eigen:#x><opcall 735:#x>;"
"<fourier:#x><opcall 736:#x>;"
"<invfourier:#x><opcall 737:#x>;"
"<RowSortAscend:#x><opcall 738:#x>;"
"<RowSortDescend:#x><opcall 739:#x>;"
"<ColumnSortAscend:#x><opcall 740:#x>;"
"<ColumnSortDescend:#x><opcall 741:#x>;"
"<copy:#x><opcall 742:#x>;"
	;

const char* libgenerator =
"<perm_rep:#x><opcall 801:#x>;"
"<combi_rep:#x><opcall 802:#x>;"
"<permutation:#x><opcall 803:#x>;"
"<combination:#x><opcall 804:#x>;"
"<permutationAll:#x><opcall 805:#x>;"
"<combinationAll:#x><opcall 806:#x>;"
	;

const char* liblink =
	"<prev_node ()>;"
	"<middle_node ()>;"
	"<next_node ()>;"
	"<prev> <prev _>;"
	"<prev #prev1>"
		"::self <prev_node (#prev1:#prev)>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"::self <setVar prev_node #prev>"
		"::self <setVar middle_node #prev1>"
		"::self <setVar next_node (#mid : #next)>"
		";"
	"<next> <next _>;"
	"<next #next1>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node (#next1 : #next)>"
		"::self <setVar prev_node (#mid : #prev)>"
		"::self <setVar middle_node #next1>"
		"::self <setVar next_node #next>"
		";"
	"<clear>"
		"::self <setVar prev_node ()>"
		"::self <setVar middle_node ()>"
		"::self <setVar next_node ()>"
		";"
	"<setlist (#list1:#list)>"
		"::self <setVar prev_node ()>"
		"::self <setVar middle_node #list1>"
		"::self <setVar next_node #list>"
		";"
	"<put #v>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"[<noteq #mid ()> ::self <setVar prev_node (#mid : #prev)>]"
		"::self <setVar middle_node #v>"
		";"
	"<get #v>"
		"::self <middle_node #v>"
		";"
	"<del>"
		"::self <prev_node (#mid : #next)>"
		"::self <setVar prev_node #next>"
		"::self <setVar middle_node #mid>"
		";"
	"<getlist #list>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"::sys <reverse #rprev #prev>"
		"(<eq #mid ()><is #l1 #rprev>"
		" |"
		" ::sys <append #l1 #rprev (#mid)>"
		")"
		"::sys <append #list #l1 #next>"
		";"
	"<length #preven>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"<let #preven = ::sys <length _ #prev> + ::sys <length _ #next> + 1>"
		";"
	"<top #n>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"(<eq #prev ()> <is #n #mid>"
		"|"
		" ::sys <reverse #rprev #prev>"
		" ::sys <car #n #rprev>"
		" ::sys <cdr #r #rprev>"
		" ::self <setVar prev_node ()>"
		" ::self <setVar middle_node #n>"
		" ::sys <append #l1 #r (#mid)>"
		" ::sys <append #l2 #l1 #next>"
		" ::self <setVar next_node #l2>"
		")"
		";"
	"<last #n>"
		"::self <prev_node #prev>"
		"::self <middle_node #mid>"
		"::self <next_node #next>"
		"(<eq #next ()> <is #n #mid>"
		"|"
		"::sys <reverse #rnext #next>"
		"::sys <car #n #rnext>"
		"::sys <cdr #r #rnext>"
		"::self <setVar next_node ()>"
		"::self <setVar middle_node #n>"
		"::sys <append #l1 #r (#mid)>"
		"::sys <append #l2 #l1 #prev>"
		"::self <setVar prev_node #l2>"
		")"
		";"
	;
