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

import hu.swankey.ammo.common.script.yunits.YClass;
import hu.swankey.ammo.common.script.yunits.YMethod;
import hu.swankey.ammo.common.yggdrasil.Yggdrasil;
import hu.swankey.ammo.common.yggdrasil.definition.ClassDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.ComplexClassDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.VariableDefinition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ComplexObject extends YContainer {
	
    private final Set<YObject> elementsInRange = new HashSet<YObject>();

    protected ComplexObject(String name, ComplexClass type) {
        super(name, type);
    }
    
    public static ComplexClass yclass() {
    	return ComplexClass.singleton;
    }
    
    
    @Override
    public void put(YObject element) {
        super.put(element);
        elementsInRange.add(element);
    }
    
    @Override
    public YObject remove(YObject element) {
        super.remove(element);
        elementsInRange.remove(element);
        return element;
    }

    @Override
    public boolean isRemovable(String name) {
    	if (getYClass() == null)
    		return true;
    	else
    		return !getYClass().hasElement(name);
    }

    @Override
    public ComplexClass getYClass(){
        return (ComplexClass) super.getYClass();
    }
    
    public YMethod getMethod(String name, Map<String, YObject> parameters){
    	return getYClass().getMethod(name, parameters);
    }
    
    public List<YMethod> getMethods(String name){
    	return getYClass().getMethods(name);
    }
    

    public boolean isInRange(YObject element) {
        return elementsInRange.contains(element);
    }

    
    @Override
    public void reportNewElementInRange(YObject element) {
        elementsInRange.add(element);
    }        
    
    @Override
    public void reportRemovedElementInRange(YObject element) {
       elementsInRange.remove(element);
    } 
    
    public void set(String id, Object value){
    	YObject element = get(id);
    	
    	if (element == null)
    		throw new RuntimeException("Element not exists: " + getPathString() + DELIMITER + id);
    	
    	if (!(element instanceof Field))
    		throw new RuntimeException("'" + id + "' in '" + getName() + "' is not a field, it is a(n) " + ((YObject)element).getYClass().getName());
    	
    	((Field) element).setValue(value);
    }
    
    
    
    public static class ComplexClass extends ContainerClass {
    	
		private static final String[] PACKAGE_PATH = { Yggdrasil.PACKAGE_EXT };
    	private static final String CLASS_NAME = "CustomObject";

    	private final HashMap<String, VariableDefinition> fields = new HashMap<String, VariableDefinition>();
        private final List<YObject> instances = new ArrayList<YObject>();

    	private static ComplexClass singleton;
    	
    	public static ComplexClass singleton(){
    	    if (singleton == null) {
    	        singleton = new ComplexClass();
    	        singleton.addSuperior(YContainer.yclass());
    	        placeClass(singleton);
    	    }
    	    
    	    return singleton;
    	}
    	
    	private ComplexClass() {
            super(CLASS_NAME, null, YObject.yclass());
        }
    	
    	public ComplexClass(String name, String[] path) {
    		super(name, (path != null ? path : PACKAGE_PATH), YObject.yclass());
    		addSuperior(singleton());
    	}


    	public void addElement(VariableDefinition def) {
    		fields.put(def.getName(), def);
    		
    		if (hasWrapperElement())
    			getWrapperElement().synchronize();
    	}
    	
      	public void removeElement(VariableDefinition def) {
    		fields.remove(def.getName());
    		
    		if (hasWrapperElement())
    			getWrapperElement().synchronize();
    	}    	
    	
    	public boolean hasElement(String id) {
    		return fields.containsKey(id);
    	}
    	
    	public VariableDefinition getElementDef(String key){
    		return fields.get(key);
    	}
    	
    	public Collection<VariableDefinition> getFields() {
    		return Collections.unmodifiableCollection(fields.values());
    	}
    	
    	public void removeElements(){
    		fields.clear();
    	}
    	
    	
		@Override
		public ClassDefinition getWrapperElement() {
			if (definition == null)
				definition = ComplexClassDefinition.yclass().create(this);
			return (ClassDefinition)definition;
		}
    	
    	


    	public Collection<YObject> createInitialElements() {
    		Collection<YObject> collection = new ArrayList<YObject>();
    		return collection;
    	}
    	
    	@Override
    	public ComplexObject create(String name) {
    		ComplexObject newComplex = new ComplexObject(name, this);
    		init(newComplex);
    		return newComplex;
    	}
    	
    	protected void init(ComplexObject complex) {
    	    createFields(complex);
            instances.add(complex);  
            System.out.println(getName() + " has " + instances.size() + " instances.");
    	}
    	
    	protected void createFields(ComplexObject complex){
            for (YClass superior: getSuperiors()){
                if (superior instanceof ComplexClass)
                    ((ComplexClass)superior).createFields(complex);
            }
            
            for (VariableDefinition def : fields.values()) {
                YObject element = def.getWrappedElement();
                complex.put(element);
            }
    	}


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