/**
 * Authors: k.inaba
 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
 *
 * Convert values between Polemy and D
 */
module polemy.valueconv;
import polemy._common;
import polemy.failure;
import polemy.ast;
import polemy.layer;
import polemy.value;
import std.string;

LexPosition extractPos( Table t )
{
	if(auto tt = t.access!Table(ValueLayer, "pos"))
	{
		auto fn = tt.access!StrValue(ValueLayer, "filename");
		auto ln = tt.access!IntValue(ValueLayer, "lineno");
		auto cl = tt.access!IntValue(ValueLayer, "column");
		if(fn !is null && ln !is null && cl !is null)
			return new LexPosition(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
	}
	return LexPosition.dummy;
}

/// Experimental!! Convert Polemy value to D Value

T polemy2d(T)(Value _v, LexPosition callpos=null)
{
	static if(is(T==BigInt))
	{
		if(auto v = cast(IntValue)_v)
			return v.data;
	}
	else
	static if(isIntegral!(T))
	{
		if(auto v = cast(IntValue)_v)
			return cast(T) v.data.toLong();
	}
	else
	static if(is(T==string))
	{
		if(auto v = cast(StrValue)_v)
			return v.data;
	}
	else
	static if(is(T S : S[]))
	{
		if(auto t = cast(Table)_v)
		{
			S[] result;
			foreach(e; t.toList())
				result ~= polemy2d!(S)(e, callpos);
			return result;
		}
	}
	else
	static if(is(T == AST))
	{
		if(auto t = cast(Table)_v)
		{
			LexPosition pos = extractPos(t);

			StrValue typ = cast(StrValue) t.access!StrValue(ValueLayer, "is");
			if( typ is null )
				throw genex!RuntimeException(callpos, text(`Invalid AST (no "is" field): `, _v));

			foreach(AT; ListOfASTTypes)
				if(typ.data == typeid(AT).name.split(".")[$-1])
				{
					typeof(AT.tupleof) mems;
					foreach(i,m; mems)
					{
						string name = AT.tupleof[i].stringof.split(".")[$-1];
						Value vm = t.access!Value(ValueLayer, name);
						if( vm is null )
							throw genex!RuntimeException(callpos, 
								text(`Invalid AST (no "`,name,`" field) for "`, typ, `" node: `, _v));
						mems[i] = polemy2d!(typeof(m))(vm, callpos);
					}
					return new AT(pos,mems);
				}
			throw genex!RuntimeException(callpos, text(`Invalid AST (unknown "is" field): `, typ));
		}
		throw genex!RuntimeException(callpos, text(`Invalid AST (not a table): `, _v));
	}
	else
	static if(is(T == class))
	{
		if(auto t = cast(Table)_v)
		{
			typeof(T.tupleof) mems;
			foreach(i,m; mems)
				mems[i] = polemy2d!(typeof(m))(t.get(T.tupleof[i].stringof.split(".")[$-1], ValueLayer), callpos);
			return new T(mems);
		}
	}
	else
		static assert(false, "unknown type <"~T.stringof~"> during polemy2d decoding");
	throw genex!RuntimeException(callpos, text("Cannot convert ",_v," to ",T.stringof));
}

/// Cons of two pairs

Table makeCons(Value a, Value d)
{
	Table t = new Table;
	t.set("car", ValueLayer, a);
	t.set("cdr", ValueLayer, d);
	return t;
}

/// Experimental!! Convert D value (except AST) to  Polemy Value

Value d2polemy(T)(T e)
{
	return ast2table(e, delegate Value(AST){ assert(false); });
}

/// Convert AST to Table so that it can be used in Polemy

Value ast2table(T)(T e, Value delegate(AST) rec)
{
	static if(is(T==BigInt) || isIntegral!(T))
		return new IntValue(e);
	else
	static if(is(Unqual!(T)==string))
		return new StrValue(e);
	else
	static if(is(T S : S[]))
	{
		Table lst = new Table;
		foreach_reverse(a; e)
			static if(is(S : AST))
				lst = makeCons(rec(a), lst);
			else
				lst = makeCons(ast2table(a,rec), lst);
		return lst;
	}
	else
	static if(is(T : AST))
	{
		assert( typeid(e) == typeid(T), text("abstracted: ", typeid(e), " vs ", typeid(T)) );
		auto t = new Table;
		if(e.pos is null) // special treatment
		{
			Table post = new Table;
			post.set("filename", ValueLayer, new StrValue("nullpo"));
			post.set("lineno", ValueLayer, new IntValue(0));
			post.set("column", ValueLayer, new IntValue(0));
			t.set("pos", ValueLayer, post);
		}
		else
			t.set("pos", ValueLayer, ast2table(e.pos,rec));
		t.set("is" , ValueLayer, new StrValue(typeid(e).name.split(".")[$-1]));
		foreach(i,m; e.tupleof)
			static if(is(typeof(m) : AST))
				t.set(e.tupleof[i].stringof.split(".")[$-1], ValueLayer, rec(m));
			else
				t.set(e.tupleof[i].stringof.split(".")[$-1], ValueLayer, ast2table(m,rec));
		return t;
	}
	else
	static if(is(T == class))
	{
		auto t = new Table;
		foreach(i,m; e.tupleof)
			static if(is(typeof(m) : AST))
				t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m));
			else
				t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec));
		return t;
	}
	else
		static assert(false, "unknown type <"~T.stringof~"> during AST encoding");
}

/// No hook version
Value ast2table(T)(T e)
{
	//[TODO] I really need to automate this!!!!!!!!!!!!1
	Value rec(AST _) {
		if(auto e = cast(Str)_) return ast2table(e, &rec);
		if(auto e = cast(Int)_) return ast2table(e, &rec);
		if(auto e = cast(Var)_) return ast2table(e, &rec);
		if(auto e = cast(Die)_) return ast2table(e, &rec);
		if(auto e = cast(Let)_) return ast2table(e, &rec);
		if(auto e = cast(Lay)_) return ast2table(e, &rec);
		if(auto e = cast(App)_) return ast2table(e, &rec);
		if(auto e = cast(Fun)_) return ast2table(e, &rec);
		assert(false);
	}
	return rec(e);
}
