/**
 * 
 */
package studio.junk.jam.coloritum;

import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;

import groovy.lang.GroovyShell;
import groovy.lang.MissingPropertyException;
import groovy.lang.Script;

/**
 * @author held03
 */
public abstract class ScriptManager<T, S extends NamedScript> {
	protected final Class<S> scriptClass;

	HashMap<String, T> types = new HashMap<String, T>();

	public void add(String name, T type) {
		types.put(name, type);
	}

	public T get(String name) {
		return types.get(name);
	}

	public ScriptManager(Class<S> type) {
		this.scriptClass = type;
	}

	protected void parseScriptsRaw(FileHandle scriptFolder, Map<?, ?> types) {
		CompilerConfiguration cc = new CompilerConfiguration();

		//cc.setScriptBaseClass("studio.junk.coffe.cala.EntityType");

		GroovyShell sh = new GroovyShell(cc);

		for (Entry<?, ?> e : types.entrySet()) {

			if (!(e.getKey() instanceof String) || !(e.getValue() instanceof String)) {
				System.err.println("Missconfigured entry: " + e.getKey().toString() + " -> " + e.getValue().toString());
				continue;
			}

			String k = (String) e.getKey();
			String v = (String) e.getValue();

			FileHandle fh = scriptFolder.child(v);

			if (!fh.exists())
				fh = scriptFolder.child(v + ".groovy");

			if (!fh.exists()) {
				System.err.println("Missconfigured entry: " + k + " -> " + fh.path() + " : script does't exist");
				continue;
			}

			try {
				Script s = sh.parse(new InputStreamReader(fh.read()));

				if (scriptClass.isInstance(s)) {

					((S) s).scriptName = fh.path();

					System.out.println("added " + k + " -> " + v + " (" + s.getClass().getSuperclass() + ")");

					addScript(k, (S) s);

				} else {
					System.err.println("Missconfigured entry: " + k + " -> " + v + " (" + s.getClass().getSuperclass()
							+ ") : no " + scriptClass.getName());
					continue;
				}
			} catch (CompilationFailedException ex) {
				System.err.println("Failed to compile script " + scriptFolder.child(v) + " : " + ex.toString());
			}


		}
	}

	protected abstract void addScript(String name, S s);

	public void parseScripts(FileHandle scriptFolder, Map<String, String> types) {
		parseScriptsRaw(scriptFolder, types);
	}

	/**
	 * 
	 */
	public void parseScripts(FileHandle scriptIndex) {

		GroovyShell shG = new GroovyShell();


		if (!scriptIndex.exists()) {
			scriptIndex = Gdx.files.getFileHandle(scriptIndex.path() + ".groovy", scriptIndex.type());
			if (!scriptIndex.exists()) {
				System.err.println("Script " + scriptIndex + " doesn't exist.");
				return;
			}
		}


		Script s;
		try {
			s = shG.parse(new InputStreamReader(scriptIndex.read()));

			if (scriptClass.isInstance(s)) {
				// handle actual script
				((S) s).scriptName = scriptIndex.path();

				addScript(scriptIndex.nameWithoutExtension(), (S) s);

			} else {
				// handle index script
				try {
					s.run();
				} catch (Exception e) {
					System.err.println("Script " + scriptIndex + " throws exception: " + e.toString());
					e.printStackTrace();
					return;
				}

				Object o = s.getBinding().getVariable("files");

				if (o instanceof Map) {
					parseScriptsRaw(scriptIndex.parent(), (Map<?, ?>) o);

				} else {
					System.err.println("Expect 'files' as Map from script " + scriptIndex + ", got " + o.getClass());
				}
			}

		} catch (MissingPropertyException e) {
			System.err.println("Script " + scriptIndex + " needs to declare 'files' as Map");
		} catch (CompilationFailedException e) {
			System.err.println("Failed to compile script " + scriptIndex + " : " + e.toString());
			return;
		}


	}

}
