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.YClassDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.ComplexYClassDefinition;
import hu.swankey.ammo.common.yggdrasil.definition.YAttributeDefinition;

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);
    }
    
    
    
    public void replace(String id, YObject newElement) {
        YObject element = get(id);
        
        if (newElement == null)
            throw new RuntimeException("'Element' cannot be null");     
        
        if (element == null)
            throw new RuntimeException("Element not exists: " + getPathString() + DELIMITER + id);
        
        YClass expectedType = getYClass().getAllowedType(); 
        YClass foundedType = newElement.getYClass();
        
        typeCheck(expectedType, foundedType, id);

        newElement.setName(id);
        newElement.setParent(this);     
        elements.add(newElement);
        elements.remove(element);
        elementsByName.put(newElement.getName(), newElement);
        

        reportChangedElement(newElement);
    }    
    
    
    
    
    
    @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 YContainerClass {
    	
		private static final String[] PACKAGE_PATH = { Yggdrasil.PACKAGE_EXT };
    	private static final String CLASS_NAME = "ComplexYClass";

    	private final HashMap<String, YAttributeDefinition> attributes = new HashMap<String, YAttributeDefinition>();
        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 addAttribute(YAttributeDefinition def) {
    		attributes.put(def.getName(), def);
    		
    		for(ComplexYObject instance: instances) {
                instance.put( def.getWrappedElement() );
    		}
    		
    		if (hasWrapperElement())
    			getWrapperElement().synchronize();
    	}
    	
      	public void removeAttribute(YAttributeDefinition def) {
    		attributes.remove(def.getName());
    		
    		for(ComplexYObject instance: instances) {
    		    instance.remove(def.getName());
    		}
    		
    		if (hasWrapperElement())
    			getWrapperElement().synchronize();
    	}    	
    	
    	public boolean hasElement(String id) {
    		return attributes.containsKey(id);
    	}
    	
    	public YAttributeDefinition getElementDef(String key){
    		return attributes.get(key);
    	}
    	
    	public Collection<YAttributeDefinition> getAttributes() {
    		return Collections.unmodifiableCollection(attributes.values());
    	}
    	
    	public void removeAttributes(){
    		attributes.clear();
    	}
    	
    	
		@Override
		public YClassDefinition getWrapperElement() {
			if (definition == null)
				definition = ComplexYClassDefinition.yclass().create(this);
			return (YClassDefinition)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 (YAttributeDefinition def : attributes.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();
    	}
    	
    }
}
