package hu.swankey.ammo.common.yggdrasil.basics;

import hu.swankey.ammo.common.script.AmmoScriptRuntimeException;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Reference extends StringField {

	private String[] path;

	public final static String ROOT_SYMBOL = "root";
	public final static String PARENT_SYMBOL = "$";
	public final static String THIS_SYMBOL = "this";
	private static final String UNNAMED_LINK = "Unnamed Link";

	public Reference(String value){
		this(UNNAMED_LINK, value);
	}
	
	public Reference(String name, String value) {
		this(name, yclass(), value);
	}
	

	protected Reference(String name, SoftLinkType type, Object value) {
		super(name, type, value);
	}
	
	public static SoftLinkType yclass(){
		return SoftLinkType.singleton();
	}
	

	@Override
	public void setValue(Object path) {
		
		// Root: root
		// This: this
		// Local: sg
		
        if (path == null)
            throw new NullPointerException("A Field's value cannot be null.");
		
		String pathString = (String) path;
				
//		if (pathString.length() > 0 && pathString.charAt(0) == YObject.DELIMITER)
//			pathString = ROOT_SYMBOL + pathString;
			
		String regex = "\\" + YObject.DELIMITER;
		
		if (pathString.endsWith(".") || pathString.startsWith(".") || pathString.contains(".."))
			throw new RuntimeException("Bad formatted link: " + pathString);
			
		
		String[] array = pathString.split(regex);
		
		for (String obj : array) {
			if (obj.isEmpty())
				throw new RuntimeException("Link has at least one empty step: \"" + pathString.toString() + "\"");
		}

		this.path = array;

		super.setValue(pathString);
	}
	
//	@Override
//	public YContainer getParent() {
//
//		if (super.getParent() != null)
//			return super.getParent();
//
//		throw new RuntimeException("Nor parent, nor owner has been set!");
//	}
	
	public String getTargetName(){
		return path[path.length-1];
	}
	
	public YContainer getTargetParent(YContainer location){
		return getTargetParent(location, null);
	}
	
	public YContainer getTargetParent(YContainer location, Map<String, YObject> env){
		return (YContainer) getTarget(location, (path.length-1), env);
	}
	
	public String[] getPathStrArray(){
		return path;
	}
	
	public List<String> getPathAsList(){
		ArrayList<String> list = new ArrayList<String>();
		for(int i=0; i<path.length; i++)
			list.add(path[i]);
		return list;
	}
	
//	public boolean isValid(YObject location, Map<String, YClass> env){
//		return getTarget(location, path.length, env) != null;
//	}
	
//	public YClass getTargetClass(YObject location, Map<String, YClass> envTypes){
//		if (isValid(location, envTypes))
//			return getTarget(location, envTypes).getYClass();
//		else
//			return null;
//	}
	
	public YObject getTarget(){
		return getTarget(getParent());
	}
	
	public YObject getTarget(YContainer location){
		return getTarget(location, null);
	}
	
	public YObject getTarget(YObject location, Map<String, YObject> env){

		YObject target = getTarget(location, path.length, env);
		
		if (target == null)		
			throw new AmmoScriptRuntimeException("Missing element: \"" + getValue() + "\" at \"" + location.getPathString() + "\"");
		
		return target;
	}
	
	
	private YObject getTarget(YObject location, int steps, Map<String, YObject> env) {

		if (location == null)
			throw new RuntimeException("Location cannot be null");

		YContainer container;
				
		YObject target = location;
		
		int i = 0;
		
		if (path[0].equals(THIS_SYMBOL))
			i++;

		while( i <steps) {
		//for (int i = 0; i < steps; i++) {
			
			if (i == 0 && env != null && env.containsKey(path[0]))
				target = env.get(path[0]);
			else {

				if (path[i].equals(ROOT_SYMBOL))
					target = target.getRoot();
				else if (path[i].equals(PARENT_SYMBOL))
					target = target.getParent();
				else {
					if (!(target instanceof YContainer))
						throw new RuntimeException("Element '" + target.getPathString() + "' not a container.");
					container = (YContainer) target;					
					target = container.get(path[i]);
				}
			}

			if (target == null)
				return null;

			i++;
		}

		return target;
	}
	
	
//	private YObject getTargetClass(YObject location, int steps, Map<String, YClass> envTypes) {
//
//		if (location == null)
//			throw new RuntimeException("Location cannot be null");
//
//		Container container;
//				
//		YObject target = location;
//		
//		int i = 0;
//		
//		if (path[0].equals(THIS_SYMBOL))
//			i++;
//
//		while( i <steps) {
//		//for (int i = 0; i < steps; i++) {
//			
//			if (i == 0 && envTypes != null && envTypes.containsKey(path[0]))
//				target = envTypes.get(path[0]);
//			else {
//
//				if (path[i].equals(ROOT_SYMBOL))
//					target = target.getRoot();
//				else if (path[i].equals(PARENT_SYMBOL))
//					target = target.getParent();
//				else {
//					if (!(target instanceof Container))
//						throw new RuntimeException("Element '" + target.getPathString() + "' not a container.");
//					container = (Container) target;					
//					target = container.get(path[i]);
//				}
//			}
//
//			if (target == null)
//				return null;
//
//			i++;
//		}
//
//		return target;
//	}	
	
//	public Element getFinalTarget(Container location) {
//		return getFinalTarget(location, null);
//	}

	public YObject getFinalTarget(YObject location, Map<String, YObject> env) {
		YObject target = getTarget(location, env);

		if (target instanceof Reference)
			return ((Reference) target).getFinalTarget(location, env);
		else
			return target;
	}
	

//	@Override
//	public Element compute(Map<String, Element> env) {
//		throw new UnsupportedOperationException();
//		//return getFinalTarget((Container)env.get(Yggdrasil.VAR_PARENT));
//	}
//	
	
	public Object clone(){
		Reference newLink = new Reference(getName());
		newLink.setValue( getValue() );
		return newLink;
	}
	

	
//	public SoftLink getParentLink(){
//		String[] newPath = new String[path.length-1];
//		
//		for(int i=0; i<newPath.length; i++)
//			newPath[i] = path[i];
//		
//		SoftLink newLink = new SoftLink();
//	}
	
	
	public static class SoftLinkType extends StringType {

		/** The name of the type */
		private static final String CLASS_NAME = "Reference";
	    private static final String DEFAULT_VALUE = "this";
		
		private static SoftLinkType singleton;
		
		public static SoftLinkType singleton() {
			if (singleton == null) {
				singleton = new SoftLinkType(CLASS_NAME);
				singleton.addSuperior( StringField.yclass() );
				placeClass(singleton);
			}
			return singleton;
		}

		protected SoftLinkType(String name) {
			super(name);
		}


		@Override
		public String getName() {
			return CLASS_NAME;
		}

	    public YObject create(String name) {
	         return new Reference(name, this, null);
	    }
	    
        @Override
        public String getDefaultValue() {
            return DEFAULT_VALUE;
        }	    

	}	
	
}
