import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;

import japa.parser.ast.expr.Expression;


class Variable{
	public final String name;
	public String type;
	private Expression definition;
	private Object value;
	private Object cls;
	public HashSet<String> dependsOn;
	
	public Variable(String name, String type, Object value){
		this.name = name;
		this.type = type;
		this.value = value;
	}
	
	public void update(Object who) {
		if(definition != null){
			set(Evaluator.evaluateExpression(definition, who));
		}
	}

	public Object get(){
		return value;
	}
	public void set(Object val){
		type = val.getClass().getName();
		
		try {
			Class wrap = Class.forName("em" + val.getClass().getName());
			System.out.println(name);
			if(value == null || (!wrap.getName().equals(value.getClass().getName()) && !wrap.isInstance(value))){
				Constructor con = wrap.getConstructor(new Class[]{val.getClass()});
				value = con.newInstance(new Object[]{val});
			}else{
				value.getClass().getDeclaredField("item").set(value, val);
			}
		} catch (Exception e) {
			value = val;
		}
		
		if(cls != null){
			try{
				try{
					String setMethod = "set" + LiveEdit.formatField(name);
					Method setV = cls.getClass().getDeclaredMethod(setMethod, new Class[]{LiveEdit.getType(val.getClass())});
					setV.setAccessible(true);
					setV.invoke(cls, new Object[]{val});
					System.out.println("Used setter function for " + name);
				}catch(Exception me){
					Field f = cls.getClass().getDeclaredField(name);
					f.setAccessible(true);
					f.set((Modifier.isStatic(f.getModifiers()) ? null : cls), val);
				}
			}catch(Exception e){
				e.printStackTrace();
				System.out.println("Error updating field in the class");
			}
		}
	}
	
	public Expression getDefinition(){
		return definition;
	}
	public void setDefinition(String def){
		definition = (Expression) Evaluator.parseExpression(def, true);
		updateDependents();
	}
	private void updateDependents(){
		dependsOn = new HashSet<String>();
		Evaluator.getDependents(definition, dependsOn);
	}
	
	public void setClass(Object obj){
		cls = obj;
	}
}