/*
 * Copyright 2009 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.lisp;

import java.beans.IntrospectionException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;

/**
 * 
 *
 *
 * @author MORIGUCHI, Yuichiro 2009
 */
public class JavaInstance extends Datum implements JavaObjective {

	//
	private Object instance;

	//
	private static final String[] JAVAPATH = {
			"net.morilib.lisp.swing"
	};

	//
	private static Class<?> applyRule1(Class<?> cl) {
		String n = cl.getName();

		for(String p : JAVAPATH) {
			try {
				return Class.forName(
						p + n.replaceFirst("^(.*)\\.J", ".Lisp"));
			} catch (ClassNotFoundException e) {
				// go next
			}
		}
		return null;
	}

	/**
	 * 
	 * @param o
	 * @return
	 */
	public static Datum newInstance(Object o) {
		Class<?> cl = (o == null) ? null : o.getClass();
		Class<?> iv;

		if(o == null || o == JavaNull.JAVA_NULL) {
			return Nil.NIL;
		} else if(o instanceof Datum) {
			return (Datum)o;
		} else if(o instanceof Integer) {
			return LispInteger.valueOf(((Integer)o).intValue());
		} else if(o instanceof Long) {
			return LispInteger.valueOf(((Long)o).longValue());
		} else if(o instanceof BigInteger) {
			return LispInteger.valueOf((BigInteger)o);
		} else if(o instanceof BigDecimal) {
			return LispUtils.bigDecimalToRational((BigDecimal)o);
		} else if(o instanceof Float) {
			return new LispDouble(((Float)o).doubleValue());
		} else if(o instanceof Double) {
			return new LispDouble(((Double)o).doubleValue());
		} else if(o instanceof String) {
			return new LispString((String)o);
		} else if(o instanceof Character) {
			return new LispCharacter(((Character)o).charValue());
		} else if(o instanceof Boolean) {
			return LispBoolean.getInstance(((Boolean)o).booleanValue());
		} else if(cl.isArray()) {
			ConsListBuilder bld = new ConsListBuilder();
			int len = Array.getLength(o);

			for(int i = 0; i < len; i++) {
				bld.append(newInstance(Array.get(o, i)));
			}
			return bld.get();
		}

		if((iv = applyRule1(cl)) != null) {
			try {
				return (Datum)iv.getConstructor(cl).newInstance(o);
			} catch (IllegalArgumentException e) {
				// go next
			} catch (InstantiationException e) {
				// go next
			} catch (IllegalAccessException e) {
				// go next
			} catch (InvocationTargetException e) {
				// go next
			} catch (NoSuchMethodException e) {
				// go next
			}
		}
		return new JavaInstance(o);
	}

	//
	/*package*/ JavaInstance(Object instance) {
		this.instance = instance;
	}

	//
	/*package*/ Object getJavaInstance() {
		return instance;
	}

	//
	/*package*/ Datum invokeMethod(
			String name, List<Datum> lst
			) throws ParameterNotFoundException {
		Object res;

		res = JavaUtils.invokeMethod(
				instance.getClass(), instance, name, lst);
		return LispUtils.toDatum(res);
	}

	//
	/*package*/ Datum invokeGetter(String name
			) throws IntrospectionException, ParameterNotFoundException {
		Object res;

		res = JavaUtils.invokeGetter(instance, name);
		return LispUtils.toDatum(res);
	}

	//
	/*package*/ void invokeSetter(String name, Datum d
			) throws IntrospectionException, ParameterNotFoundException {
		JavaUtils.invokeSetter(instance, name, d);
	}

	//
	/*package*/ Datum invokeGetter(String name, int index
			) throws IntrospectionException, ParameterNotFoundException {
		Object res;

		res = JavaUtils.invokeGetter(instance, name, index);
		return LispUtils.toDatum(res);
	}

	//
	/*package*/ void invokeSetter(String name, int index, Datum d
			) throws IntrospectionException, ParameterNotFoundException {
		JavaUtils.invokeSetter(instance, name, index, d);
	}

	/* (non-Javadoc)
	 * @see net.morilib.lisp.JavaObjective#toObject()
	 */
	public Object toObject() {
		return instance;
	}

	/* (non-Javadoc)
	 * @see net.morilib.lisp.Datum#toDisplayString(java.lang.StringBuilder)
	 */
	@Override
	public void toDisplayString(StringBuilder buf) {
		Object o = getJavaInstance();

		buf.append("#<java-instance ");
		buf.append(o.getClass().getName());
		buf.append(">");
	}

}
