package yak;

import java.util.Hashtable;

import yak.Pro.Obj;
import yak.Talk.Terp.Frame;

public class Cls extends Obj {
	Terp terp;
	String name;
	Cls supercls;
	Hashtable<String, Meth> meths;

	Cls(Cls cls, Terp terp, String name, Cls supercls) {
		super(cls);
		this.terp = terp;
		this.name = name;
		this.supercls = supercls;
		this.meths = new Hashtable<String, Meth>();
	}
	Cls asCls() {
		return this;
	}

	static Hashtable<String, Meth> builtinClsMethods(final Terp t) {
		Hashtable<String, Meth> z = new Hashtable<String, Meth>();
		z.put("nam", new JavMeth(t.tJavMeth) {
			public Pro eval(Frame f, Pro r, Pro[] args) {
				Cls rcls = r.asCls();
				assert rcls != null;
				return new Str(t, rcls.name);
			}
		});
		z.put("subc:", new JavMeth(t.tJavMeth) {
			public Pro eval(Frame f, Pro r, Pro[] args) {
				assert args.length == 1;
				Str newnameStr = args[0].asStr();
				assert newnameStr != null;
				String newname = newnameStr.str;
				Cls newMetaCls = new Cls(t.tMetacls, t, newname + "Cls", r.cls);
				Cls newCls = new Cls(newMetaCls, t, newname, r.asCls());
				t.glob.dict.put(newMetaCls.name.toLowerCase(), newMetaCls);
				t.glob.dict.put(newCls.name.toLowerCase(), newCls);
				return newCls;
			}
		});
		z.put("meth:as:", new JavMeth(t.tJavMeth) {
			public Pro eval(Frame f, Pro r, Pro[] args) {
				assert args.length == 2;
				
				Str methNameStr = args[0].asStr();
				assert methNameStr != null;
				String methName = methNameStr.str;
				
				Str methBodyStr = args[1].asStr();
				assert methBodyStr != null;
				String methBody = methBodyStr.str;
				
				Expr body = Parser.parse(t, methBody);
				UsrMeth m = new UsrMeth(r.asCls(), methBody, body);
				r.asCls().meths.put(methName.toLowerCase(), m);
				return m;
			}
		});
		return z;
	}
	
	public static abstract class Meth extends Obj {
		public Meth(Cls cls) {
			super(cls);
		}
		public abstract Pro eval(Frame f, Pro r, Pro[] args);
	}

	public static abstract class JavMeth extends Meth {
		JavMeth(Cls cls) {
			super(cls);
		}
		public abstract Pro eval(Frame f, Pro r, Pro[] args);
	}
	public static class UsrMeth extends Meth {
		String src;
		Expr expr;
		UsrMeth(Cls cls, String src, Expr expr) {
			super(cls);
			this.src = src;
			this.expr = expr;  // TODO: when we have a Parser, generate this.
		}
		@Override
		public Pro eval(Frame f, Pro r, Pro[] args) {
			Frame f2 = f.terp().newFrame(f, null);
			f2.dict.put("self", r);
			for (int i = 0; i < args.length; i++) {
				f2.dict.put(new Character((char)('a' + i)).toString(), args[i]);
			}
			Pro z = expr.eval(f2);
			return z;
		}
	}
}
