/*******************************************************************************
 * Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is made available under the terms of the
 * Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Exadel, Inc. and Red Hat, Inc. - initial API and implementation
 ******************************************************************************/ 
package org.jboss.tools.common.model.impl;

import java.text.MessageFormat;
import java.util.*;

import org.eclipse.swt.widgets.Display;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.meta.*;
import org.jboss.tools.common.model.event.XModelTreeEvent;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.XModelObjectLoaderUtil;

public class RegularObjectImpl extends XModelObjectImpl implements XOrderedObject {
    private static final long serialVersionUID = 7942041044569562286L;
    protected RegularChildren children = null;

    public RegularObjectImpl() {}

    protected RegularChildren createChildren() {
        return new RegularChildren();
    }

    public void destroy() {
    	if(children != null && !children.isEmpty()) {
    		XModelObject[] h = children.getObjects();
    		for (XModelObject o: h) {
    			((XModelObjectImpl)o).destroy();
    		}
    		//TODO provide safe work at setting children to null.
    		children = createChildren();
    	}
    	super.destroy();
    }

    public boolean areChildrenOrdered() {
    	return children != null && children.areChildrenOrdered();
    }

    public String name() {
        return get(XModelObjectConstants.XML_ATTR_NAME);
    }

    protected void onSetEntity(String name) {
        super.onSetEntity(name);
        if(children == null) {
        	children = createChildren();
        }
        children.clear();
        children.setComparator(createComparator());
    }

    protected Comparator<XModelObject> createComparator() {
        return EntityComparator.getComparator(getModelEntity());
    }

    public boolean hasChildren() {
        return !children.isEmpty();
    }

    protected void loadChildren() {}

    public XModelObject[] getChildren() {
        loadChildren();
        return children.getObjects();
    }
    
    public XModelObject[] getLoadedChildren() {
    	return children.getObjects();
    }

    public boolean addChild_0(XModelObject o) {
    	if(o == null) {
    		return false;
    	}
    	String entity = o.getModelEntity().getName();
    	XChild c = getModelEntity().getChild(entity);
        if(c == null) return false;
        if(c.getMaxCount() < Integer.MAX_VALUE && children.getChildrenCount(entity) >= c.getMaxCount()) {
        	return false;
        }
        ((XModelObjectImpl)o).setParent_0(this);
        boolean b = children.addObject(o);
        if(b) {
        	if(o.getErrorState() == 2 || o.getErrorChildCount() > 0) {
        		registerErrorChild();
        	}
        	if(o.getErrorState() == 1 || o.getWarningChildCount() > 0) {
        		registerWarningChild();
        	}
        }
        return b;
    }

    public void removeChild_0(XModelObject o) {
        if(children.removeObject(o)) {
        	if(o.getErrorState() == 2 || o.getErrorChildCount() > 0) {
        		unregisterErrorChild();
        	}
        	if(o.getErrorState() == 1 || o.getWarningChildCount() > 0) {
        		unregisterWarningChild();
        	}
        }
    }

    public void set(String name, String value) {
        if(!(getParent() instanceof RegularObjectImpl)) {
        	super.set(name, value);
        	return;
        }
        String ov = super.get(name);
        String opp = getPathPart();
        super.set(name, value);
        String npp = getPathPart();
        if(opp != null && opp.equals(npp)) {
        	return;
        }
        RegularObjectImpl p = (RegularObjectImpl)getParent();
        XModelObject c = p.children.change(this, opp, npp);
        if(c != null) {
        	if(getModelEntity().getAttribute(XModelObjectLoaderUtil.ATTR_ID_NAME) != null) {
        		int k = 1;
        		while(c != null) {
        			super.set(XModelObjectLoaderUtil.ATTR_ID_NAME, "" + k); //$NON-NLS-1$
        			npp = getPathPart();
        			c = p.children.change(this, opp, npp);
        			k++;
        		}
        		return;
        	} else if(hasIdAttr()) {
        		int k = 1;
        		while(c != null) {
        			super.set(XModelObjectImpl.DUPLICATE, "" + k); //$NON-NLS-1$
        			npp = getPathPart();
        			if(k == 1 && npp.indexOf(XModelObjectImpl.DUPLICATE) < 0) {
        				elementExists(c, name, value);
        				return;
        			}
        			c = p.children.change(this, opp, npp);
        			k++;
        		}
        	} else {
        		if(ov == null) properties.remove(name); else super.set(name, ov);
        		elementExists(c, name, value);
        	}
        }
    }

    public XModelObject getChildByPathPart(String pathpart) {
        loadChildren();
        return children.getObject(pathpart);
    }

    // diagnostic

    /**
     * This method may be called in two cases:
     * 1) User input is not appropriately validated. In this case proper validation 
     *    and notification should be implemented (e.g. error message in dialogs, 
     *    error markers in editors/error view)
     * 2) Internal model update is implemented with errors.
     * @param o
     * @param name
     * @param value
     */
    protected void elementExists(XModelObject o, String name, String value) {
        if(getModel().getService() == null) return;
        String mes = MessageFormat.format(
				"Cannot set {0} = {1} for {2}\nbecause {3} exists in the {4}",
				getAttrNameByXMLName(name), value, title(this), title(o), title(getParent()));
        if(Display.getCurrent() != null) {
        	getModel().getService().showDialog("Error", mes, new String[]{"OK"}, null, 1);
        }
       	ModelPlugin.getDefault().logError(mes);
    }

    private static String title(XModelObject o) {
        return o.getAttributeValue(XModelObjectConstants.ATTR_ELEMENT_TYPE) + " " + //$NON-NLS-1$
               o.getModelEntity().getRenderer().getTitle(o);
    }

    private String getAttrNameByXMLName(String name) {
        if(getModelEntity().getAttribute(name) != null) return name;
        XAttribute[] as = getModelEntity().getAttributes();
        for (int i = 0; i < as.length; i++)
          if(name.equals(as[i].getXMLName())) return as[i].getName();
        return name;
    }

    // implementation of interface XOrderedObject for ordered subclasses

    public int getIndexOfChild(XModelObject o) {
        return children.getIndex(o);
    }

    public boolean move(int from, int to, boolean firechange) {
        boolean b = children.move(from, to);
        if(b) {
            fireStructureChanged(XModelTreeEvent.STRUCTURE_CHANGED, this);
            if(firechange) setModified(true);
        }
        return b;
    }
    
    //TODO implement registering/unregistering error children 
    public void replaceChildren(XModelObject[] objects) {
    	for (int i = 0; i < objects.length; i++) {
    		((XModelObjectImpl)objects[i]).setParent_0(this);
    	}
    	children.replaceChildren(objects);
    	changeTimeStamp();
    }

}

