package ch.wlkl.shell;

import static ch.wlkl.shell.Top.sFail;

import java.io.File;
import java.io.FileInputStream;
import java.io.PrintStream;
import java.util.ArrayList;

public class JavaCmpLoad<T> extends ClassLoader {

	public final JavaCmpLoad<?> parent;
	public final File root;
	public final boolean temporary;
	public final Object pckg;
	public final Class<T> superCla;

	private int uniqueNum = 0;
	
	public JavaCmpLoad(JavaCmpLoad<?> pa, Class<T> sup) {
		this(pa, null, null, null, sup);
	}

	@SuppressWarnings("unchecked")
	public JavaCmpLoad(JavaCmpLoad<?> par, String rt, Boolean tmp, Object pac, Class<T> sup) {
		root = Ut.canonical(new File(rt != null ? rt : par != null ? par.root.getAbsolutePath() : "."));
		temporary = tmp != null ? tmp : par != null ? par.temporary : false;
		pckg = pac != null ? pac : par != null ? par.pckg : "temporary.shell";
		superCla = sup != null ? sup : par != null ? (Class<T>) par.superCla : null;
		while (par != null && par.parent != null)
			par = par.parent;
		parent = par;
		if (! (root.exists() && root.isDirectory()))
			root.mkdirs();
	}

	public String toString() {
		return this.getClass().getName() + "(" + (parent == null ? "uniqueNum=" + uniqueNum : "parent.uniqueNum=" + parent.uniqueNum)+ ", root=" + root + ", temporary=" + temporary + ")";
	}

	public static String className(Object o) {
		try {
			return (String) o;
		} catch (ClassCastException e) {
			return ((Class) o).getSimpleName();
		}
	}
	/** the imports for each class, an element i of the array is translated to the following source:
	 * <ul>¨
	 * <li> import ((String) i) ;
	 * <li> import (((Class) i).getName()) ;
	 * <li> import (((Package) i).getName()).* ;
	 * <ul> */
	
	public static CharSequence[] classSource(Object pk, Object[] imports, String cla, Object sup, Object[] implem, CharSequence... body) {
		ArrayList<CharSequence> l = new ArrayList<CharSequence>();
		String pr = null;
		l.add("package " + pk + ';');
		for (Object i: imports) {
			try {
				pr = (String) i;
			} catch (ClassCastException e1) {
				try {
					pr = ((Class) i).getName();
				} catch (ClassCastException e2) {
					try {
						pr = ((Package) i).getName() + ".*";
					} catch (ClassCastException e3) {
						sFail("bad javaImport[?] " + i);
					}						
				}					
			}
			l.add("\nimport " + pr + ';');
		}
		l.add("\npublic class " + cla);
		if (sup != null && ! sup.equals(""))
			l.add(" extends " + className(sup));
		pr = " implements ";
		for (Object e : implem) {
			if (e != null && ! e.equals("")) {
				l.add(pr + className(e));
				pr = ", ";
			}				
		}
		l.add(" {");
		for (CharSequence s: body) {
			if (s != null) {
				if (s.length() > 0 && s.charAt(0) != '\n')
					l.add("\n");
				l.add(s);
			}
		}
		l.add("\n}");
		return l.toArray(new CharSequence[l.size()]);
	}

	public CharSequence[] classSource(Object[] imports, String cla, Object[] implem, CharSequence... body) {
		return classSource(pckg, imports, cla, superCla, implem, body);
	}

	@SuppressWarnings("unchecked")
	public <C> Class<? extends C> makeClass(File rt, Boolean tmp, Object pk, String cla, Class<C> sup, CharSequence... source) {
		int dx = cla.lastIndexOf('.');
		rt = rt == null ? root : rt;
		sup = sup == null ? (Class<C>) superCla : sup;
		if (dx >= 0) {
			pk = cla.substring(0, dx);
			cla = cla.substring(dx+1);
		}
		tmp = tmp != null ? tmp : pk != null ? false : temporary;
		pk = pk == null ? pckg : pk;
		File dir = new File(rt, pk.toString().replace('.', '/'));
		File srcFile = new File(dir, cla + ".java");
		File claFile = new File(dir, cla + ".class");
		if (! dir.isDirectory())
			if (! dir.mkdirs())
				sFail("could not create directory " + dir);
		try {
			srcFile.delete();
			claFile.delete();
			PrintStream ps = new PrintStream(srcFile);
			for(CharSequence s : source)
				ps.print(s);
			ps.println("");
			ps.close();
			String [] args = {srcFile.getAbsolutePath()};
			int res = com.sun.tools.javac.Main.compile(args, new java.io.PrintWriter(System.out));
			if (res != 0)
				sFail("compile rc " + res + " errors for class " + pk + '.' + cla);
			FileInputStream f = new FileInputStream(claFile);
			int len = f.available();
			byte[] byteCode = new byte[len];
			f.read(byteCode);
			f.close();
			Class<? extends C> cl = defineClass(pk.toString() + '.' + cla, byteCode, 0, len).asSubclass(sup);
			if (tmp) {
				srcFile.delete();
				claFile.delete(); 
			}
			return cl;
		} catch (Exception e) {
			e.printStackTrace();
			sFail("define class " + pk + '.' + cla + " in dir " + dir);
			return null;
		} 
	}

	public Class<? extends T> makeClass(String cla, CharSequence... source) {
		return makeClass(null, null, null, cla, null, source);
	}

	public int uniqueNum() {
		return parent == null ? ++uniqueNum : parent.uniqueNum();
	}

	public static String uniqueName(String pref, int i) {
		return pref + i;
	}

	public String uniqueName(String pref) {
		return uniqueName(pref, uniqueNum());
	}

}






































































































