package test; 

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.CharBuffer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import se.lth.control.labcomm2014.Decoder;
import se.lth.control.labcomm2014.DecoderChannel;
import se.lth.control.labcomm2014.Encoder;
import se.lth.control.labcomm2014.EncoderChannel;
import AST.Parser;
import AST.Scanner;
import AST.Specification;
import beaver.Parser.Exception;



public class TestCompiler {

	private static final String BAR = "bar";
	private static final String FOO = "foo";

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		/* input data: */
		FileReader fr;
		int len=0;;
		CharBuffer buf = CharBuffer.allocate(1024);
		try {
			fr = new FileReader(args[0]);
			len = fr.read(buf);
			buf.rewind();
		} catch (Throwable e) {
			e.printStackTrace();
			System.exit(1);
		}
		
		String labcommStr = buf.toString().substring(0, len-1);
		
		HashMap <String, String> handlers = new HashMap<String, String>();

		handlers.put(FOO, "public void handle_"+FOO+"("+FOO+" value) {\nSystem.out.println(value.x);\nSystem.out.println(value.y);\nSystem.out.println(value.z);}");
		handlers.put(BAR, "public void handle_"+BAR+"(int value) {System.out.println(value);}");

		InRAMCompiler irc = generateCode(labcommStr, handlers);

		if(irc != null) {
			System.out.println("*** Testing instantiation and invocation of Handler ");
			dummyTest(irc);
			
//			String tmpFile = "/tmp/lcctest";
			String tmpFile = args[1];
			System.out.println("*** Testing writing and reading file "+tmpFile);
			encodeTest(irc, tmpFile);
			decodeTest(irc, tmpFile);
		}
	}
	private static void decodeTest(InRAMCompiler irc, String tmpFile) {
		try {
			FileInputStream in = new FileInputStream(tmpFile);
			DecoderChannel dec = new DecoderChannel(in);
	
			Class fc = irc.load(FOO);
			Class hc = irc.load("gen_"+FOO+"Handler");
			Class hi = irc.load(FOO+"$Handler");

			Object h = hc.newInstance(); 
		
			Method reg = fc.getDeclaredMethod("register", Decoder.class, hi);
			reg.invoke(fc, dec, h);
			
			dec.runOne();
			in.close();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
	}
	private static void encodeTest(InRAMCompiler irc, String tmpFile) {
		Class<?> hc;
		try {
			hc = irc.load("gen_"+FOO+"Handler");
			Object h = hc.newInstance(); 
			Class fc = irc.load(FOO);
			Object f = fc.newInstance();
			Field x = fc.getDeclaredField("x");
			Field y = fc.getDeclaredField("y");
			Field z = fc.getDeclaredField("z");
			x.setInt(f, 10);
			y.setInt(f, 11);
			z.setInt(f, 12);
			
			FileOutputStream out = new FileOutputStream(tmpFile);
			EncoderChannel enc = new EncoderChannel(out);
			Method reg = fc.getDeclaredMethod("register", Encoder.class);
			reg.invoke(fc, enc);
			
			Method doEncode = fc.getDeclaredMethod("encode", Encoder.class, fc);
			doEncode.invoke(fc, enc, f);
			
			out.close();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	public static InRAMCompiler generateCode(String lcDecl, HashMap<String, String> handlers) {
		Specification ast = null;
		InputStream in = new ByteArrayInputStream(lcDecl.getBytes());
		Scanner scanner = new Scanner(in);
		Parser parser = new Parser();
		Collection errors = new LinkedList();

		InRAMCompiler irc = null;
		try {
			Specification p = (Specification)parser.parse(scanner);
			p.errorCheck(errors);
			if (errors.isEmpty()) {
				ast = p;
			} else {
				System.out.println("*** Errors:");
				for (Iterator iter = errors.iterator(); iter.hasNext(); ) {
					String s = (String)iter.next();
					System.out.println(s);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (ast != null) {
			irc = handleAst(ast, handlers);
		} else {
			System.err.println("compilation failed");
		}
		return irc;
	}
	/* dummy test creating instances of sample and handler, and calling handle*/
	private static void dummyTest(InRAMCompiler irc) {
		try {
			Class hc = irc.load("gen_"+FOO+"Handler");
			Object h = hc.newInstance(); 
			Class fc = irc.load(FOO);
			Object f = fc.newInstance();
			Field x = fc.getDeclaredField("x");
			Field y = fc.getDeclaredField("y");
			Field z = fc.getDeclaredField("z");
			x.setInt(f, 10);
			y.setInt(f, 11);
			z.setInt(f, 12);
			Method m;
			try {
				m = hc.getDeclaredMethod("handle_"+FOO, fc);
				m.invoke(h, f);
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/** generate labcomm code and compile handlers 
	 *
	 * @param lcAST - the AST of the labcomm declaration
	 * @param handlers - a map <name, source> of handlers for the types in ast
	 * @return an InRAMCompiler object containing the generated clases
	 */
	private static InRAMCompiler handleAst(Specification lcAST, HashMap<String, String> handlers) {
		Map<String, String> genCode = new HashMap<String, String>();
		try {
			lcAST.J_gen(genCode, "labcomm.generated", 2013);
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Generated labcomm code:");

		InRAMCompiler irc = new InRAMCompilerJavax("labcomm.generated", null);

		Iterator<String> i = genCode.keySet().iterator();
		while(i.hasNext()){
			final String sampleName = i.next();
			final String src = genCode.get(sampleName);
			System.out.println("***"+sampleName+"\n"+src);
			StringBuilder sb = new StringBuilder();
			sb.append("package labcomm.generated;\n");
			sb.append("public class gen_"+sampleName+"Handler implements "+sampleName+".Handler {\n");
			sb.append(handlers.get(sampleName));
			sb.append("}\n");
			System.out.println("-------------------------------------");
			System.out.println(sb.toString());
			try {
				irc.compile(sampleName, src);
				irc.compile("gen_"+sampleName+"Handler", sb.toString());
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
			System.out.println("================================");
		}
		return irc;
	}
}