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 ComplexYObject extends YContainer {
	
    private final Set<YObject> elementsInRange = new HashSet<YObject>();

    protected ComplexYObject(String name, ComplexYClass type) {
        super(name, type);
    }
    
    public static ComplexYClass yclass() {
    	return ComplexYClass.singleton;
    }
    
    /** Set the element's parent */
    protected void setParent(YContainer newParent) {
        super.setParent(newParent);
        
        if (newParent == null)
            getYClass().uninit(this);
    }
    
    
    @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 ComplexYClass getYClass(){
        return (ComplexYClass) 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 SimpleObject))
    		throw new RuntimeException("'" + id + "' in '" + getName() + "' is not a field, it is a(n) " + ((YObject)element).getYClass().getName());
    	
    	((SimpleObject) element).setValue(value);
    }
    
    
    
    public static class ComplexYClass 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<ComplexYObject> instances = new ArrayList<ComplexYObject>();

    	private static ComplexYClass singleton;
    	
    	public static ComplexYClass singleton(){
    	    if (singleton == null) {
    	        singleton = new ComplexYClass();
    	        singleton.addSuperior(YContainer.yclass());
    	        placeClass(singleton);
    	    }
    	    
    	    return singleton;
    	}
    	
    	private ComplexYClass() {
            super(CLASS_NAME, null, YObject.yclass());
        }
    	
    	public ComplexYClass(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);
    		
    		for(ComplexYObject instance: instances) {
                instance.put( def.getWrappedElement() );
    		}
    		
    		if (hasWrapperElement())
    			getWrapperElement().synchronize();
    	}
    	
      	public void removeElement(VariableDefinition def) {
    		fields.remove(def.getName());
    		
    		for(ComplexYObject instance: instances) {
    		    instance.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 ComplexYObject create(String name) {
    		ComplexYObject newComplex = new ComplexYObject(name, this);
    		init(newComplex);
    		return newComplex;
    	}
    	
    	protected void init(ComplexYObject complex){
            for (YClass superior: getSuperiors()){
                if (superior instanceof ComplexYClass)
                    ((ComplexYClass)superior).init(complex);
            }
            
            for (VariableDefinition def : fields.values()) {
                YObject element = def.getWrappedElement();
                
                if (complex.containsKey(element.getName())) {
                    System.out.println("Conflict in class '" + getName() + "' at attribute '" + element.getName() + "'");
                } else {
                    complex.put(element);
                }
            }
            
            instances.add(complex);
            System.out.println(getName() + " has " + instances.size() + " instances.");
    	}
    	
    	protected void uninit(ComplexYObject complex){
    	    instances.remove(complex);
    	    System.out.println(getName() + " has " + instances.size() + " instances.");
    	}


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