/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.nina;

import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.morilib.automata.DFA;
import net.morilib.util.CharCodes;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/10/13
 */
public final class Nina {

	/**
	 * 
	 */
	public static final String VERSION = "0.3.1";

	//
	private static final Pattern FANM = Pattern.compile(
			"(.*/)?([^/.]+)(\\..*)?");

	//
	private Nina() {}

	//
	static Object prendClasse(String s) {
		return prendClasse("", s);
	}

	//
	static Object prendClasse(String d, String n) {
		String s = n;
		Class<?> c;

		try {
			if(s.indexOf('.') < 0)  s = d + "." + n;
			c = Class.forName(s);
			return c.newInstance();
		} catch(ClassNotFoundException e) {
			throw new NinaException(e, "reflecterror");
		} catch(InstantiationException e) {
			throw new NinaException(e, "reflecterror");
		} catch(IllegalAccessException e) {
			throw new NinaException(e, "reflecterror");
		}
	}

	//
	@SuppressWarnings({ "rawtypes", "unchecked" })
	static void apelle(Object o, String s, Object... os) {
		Class[] a = new Class[os.length];
		Class c = o.getClass();
		Method m;

		try {
			for(int i = 0; i < os.length; i++) {
				a[i] = os[i].getClass();
			}
			m = c.getMethod(s, a);
			m.invoke(o, os);
		} catch(SecurityException e) {
			throw new NinaException(e, "reflecterror");
		} catch(NoSuchMethodException e) {
			throw new NinaException(e, "reflecterror");
		} catch(IllegalArgumentException e) {
			throw new NinaException(e, "reflecterror");
		} catch(IllegalAccessException e) {
			throw new NinaException(e, "reflecterror");
		} catch(InvocationTargetException e) {
			throw new NinaException(e, "reflecterror");
		}
	}

	//
	@SuppressWarnings({ "rawtypes", "unchecked" })
	static void apelleInt(Object o, String s, int n) {
		Class c = o.getClass();
		Method m;

		try {
			m = c.getMethod(s, Integer.TYPE);
			m.invoke(o, n);
		} catch(SecurityException e) {
			throw new NinaException(e, "reflecterror");
		} catch(NoSuchMethodException e) {
			throw new NinaException(e, "reflecterror");
		} catch(IllegalArgumentException e) {
			throw new NinaException(e, "reflecterror");
		} catch(IllegalAccessException e) {
			throw new NinaException(e, "reflecterror");
		} catch(InvocationTargetException e) {
			throw new NinaException(e, "reflecterror");
		}
	}

	static int prendCharcode(String charset, int c) {
		int x;

		try {
			x = CharCodes.unicodeToOtherCode((char)c, charset);
			return x;
		} catch (UnsupportedEncodingException e) {
			throw new NinaException(e, "unsupportedcharset",
					charset);
		}
	}

	/**
	 * 
	 * @param s
	 * @return
	 */
	public static String getFAName(String s) {
		Matcher m;

		if(!(m = FANM.matcher(s)).matches()) {
			throw new RuntimeException();
		} else {
			return m.group(2);
		}
	}

	/**
	 * 
	 * @param s
	 * @param prms
	 * @param sub
	 * @return
	 */
	public static NinaAction compile(String name, String s,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons) {
		return NinaParser.compile(Quadro.read(name, s), null, null,
				prms, sub, cons, 0);
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @param libs
	 * @param prms
	 * @param sub
	 * @return
	 * @throws IOException
	 */
	public static NinaAction compile(String name, Reader rd,
			List<String> libs, Map<String, String> prms,
			Map<String, Object> sub,
			NinaConsole cons) throws IOException {
		return NinaParser.compile(Quadro.read(name, rd), null, libs,
				prms, sub, cons, 0);
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @param prms
	 * @param sub
	 * @return
	 * @throws IOException
	 */
	public static NinaAction compile(String name, Reader rd,
			Map<String, String> prms,
			Map<String, Object> sub,
			NinaConsole cons) throws IOException {
		return NinaParser.compile(Quadro.read(name, rd), null, null,
				prms, sub, cons, 0);
	}

	/**
	 * 
	 * @param name
	 * @param prms
	 * @param sub
	 * @return
	 * @throws IOException
	 */
	public static NinaAction compileResource(String name,
			Map<String, String> prms,
			Map<String, Object> sub,
			NinaConsole cons) throws IOException {
		return NinaParser.compile(Quadro.readResource(name),
				null, null, prms, sub, cons, 0);
	}

	/**
	 * 
	 * @param s
	 * @param prms
	 * @param sub
	 * @param pause
	 * @return
	 */
	public static NinaAction trace(String name, String s,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean pause) {
		return NinaParser.compile(Quadro.read(name, s), null, null,
				prms, sub, cons,
				NinaParser._PRINT | (pause ? NinaParser._PAUSE : 0));
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @param prms
	 * @param sub
	 * @param pause
	 * @return
	 * @throws IOException
	 */
	public static NinaAction trace(String name, Reader rd,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean pause) throws IOException {
		return NinaParser.compile(Quadro.read(name, rd), null, null,
				prms, sub, cons,
				NinaParser._PRINT | (pause ? NinaParser._PAUSE : 0));
	}

	/**
	 * 
	 * @param name
	 * @param prms
	 * @param sub
	 * @param pause
	 * @return
	 * @throws IOException
	 */
	public static NinaAction traceResource(String name,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean pause) throws IOException {
		return NinaParser.compile(Quadro.readResource(name),
				null, null, prms, sub, cons,
				NinaParser._PRINT | (pause ? NinaParser._PAUSE : 0));
	}

	//
	@SuppressWarnings("unchecked")
	private static NinaPattern cst(Object o) {
		if(o instanceof NinaNFA) {
			return new NinaNFAPattern((NinaNFA)o);
		} else if(o instanceof DFA) {
			return new NinaDFAPattern((DFA<Object, Object, Void>)o);
		} else {
			throw new ClassCastException();
		}
	}

	/**
	 * 
	 * @param name
	 * @param s
	 * @param prms
	 * @param sub
	 * @return
	 */
	public static NinaPattern pattern(String name, String s,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons) {
		return cst(compile(name, s, prms, sub, cons).getMachine());
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @param prms
	 * @param sub
	 * @return
	 * @throws IOException
	 */
	public static NinaPattern pattern(String name, Reader rd,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons) throws IOException {
		return cst(compile(name, rd, prms, sub, cons));
	}

	/**
	 * 
	 * @param name
	 * @param prms
	 * @param sub
	 * @return
	 * @throws IOException
	 */
	public static NinaPattern patternResource(String name,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons) throws IOException {
		return cst(compileResource(name, prms, sub, cons).getMachine());
	}

	/**
	 * 
	 * @param s
	 * @param prms
	 * @param sub
	 * @param z
	 * @return
	 */
	public static NinaPattern patternTrace(String name, String s,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean z) {
		return cst(trace(name, s, prms, sub, cons, z));
	}

	/**
	 * 
	 * @param name
	 * @param rd
	 * @param prms
	 * @param sub
	 * @param z
	 * @return
	 * @throws IOException
	 */
	public static NinaPattern patternTrace(String name, Reader rd,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean z) throws IOException {
		return cst(trace(name, rd, prms, sub, cons, z));
	}

	/**
	 * 
	 * @param name
	 * @param prms
	 * @param sub
	 * @param z
	 * @return
	 * @throws IOException
	 */
	public static NinaPattern patternResourceTrace(String name,
			Map<String, String> prms, Map<String, Object> sub,
			NinaConsole cons, boolean z) throws IOException {
		return cst(traceResource(name, prms, sub, cons, z));
	}

}
