/*******************************************************************************
 * 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.engines.impl;

import java.io.File;
import java.util.*;
import org.jboss.tools.common.meta.*;
import org.jboss.tools.common.model.*;
import org.jboss.tools.common.model.impl.*;
import org.jboss.tools.common.model.loaders.impl.*;
import org.jboss.tools.common.model.util.XModelObjectLoaderUtil;

public class EnginesLoader extends URLRootLoader {

    public EnginesLoader() {}

    public void load(XModelObject object) {
        super.load(object);
        XChild[] cs = object.getModelEntity().getChildren();
        for (int i = 0; i < cs.length; i++) {
          if(!cs[i].isRequired() || cs[i].getMaxCount() != 1) continue;
          if(object.getChildren(cs[i].getName()).length > 0) continue;
          //April 2008: catch removed. This call is proved to be safe.
          object.addChild(XModelObjectLoaderUtil.createValidObject(object.getModel(), cs[i].getName()));
        }
        if(isFilePath(getPath(object))) {
            File f = file(object);
            if(f.isFile()) {
                object.getModel().getFileRegistry().register(f);
            } else {
                object.getModel().getFileRegistry().unregister(f);
            }
        }
    }

    public boolean save(XModelObject object) {
        boolean b = super.save(object);
        if(b && isFilePath(getPath(object))) {
            File f = file(object);
            if(f.isFile()) object.getModel().getFileRegistry().register(f);
        }
        return b;
    }

    public boolean update(XModelObject object) throws XModelException {
        if(!isFilePath(getPath(object))) return true;
        File f = file(object);
        if(!object.getModel().getFileRegistry().isUpdated(f)) return true;
        XModelObject c = object.copy(0);
        load(c);
       	merge(object, c);
        return true;
    }

    public String fileroot(XModelObject object) {
        return XModelConstants.getWorkspace(object.getModel()) + XModelObjectConstants.SEPARATOR;
    }

    protected String fileName(XModelObject object) {
        return object.getModelEntity().getName().toLowerCase() + ".rex"; //$NON-NLS-1$
    }

	public static void merge(XModelObject object, XModelObject update) throws XModelException {
		merge(object, update, object.isActive());
	}

    public static void merge(XModelObject object, XModelObject update, boolean fire) throws XModelException {
		XModelObjectLoaderUtil.mergeAttributes(object, update, fire);
        Map<String,XModelObject> map = getChildrenForSaveAsMap(object);
        Set<String> set = null;
        XModelObject[] cs = update.getChildren();
        for (int i = 0; i < cs.length; i++) {
            XModelObject c = object.getChildByPath(cs[i].getPathPart());
            if(c == null) {
            	if(set == null) set = getChildrenToRemove(map, update);
            	c = findAppropriateChild(set, cs[i], map);
            	if(c == null) {
            		c = cs[i].copy();
                    object.addChild(c);
            	} else {
            		boolean has_id = c.getModelEntity().getAttribute(XModelObjectLoaderUtil.ATTR_ID_NAME) != null
            			|| ((XModelObjectImpl)c).hasIdAttr();
            		if(has_id) {
            			c.removeFromParent();
            			EnginesLoader.merge(c, cs[i], false);
            			if(!object.addChild(c)) {
            				c.set(XModelObjectImpl.DUPLICATE, "1234");
            				object.addChild(c);
            			}
            			if(c.get(XModelObjectImpl.DUPLICATE) != null) {
            				c.set(XModelObjectImpl.DUPLICATE, "");
            			}
            		} else {
            			EnginesLoader.merge(c, cs[i], fire);
            		}
            	}
            } else if(c.getModelEntity().getName().equals(cs[i].getModelEntity().getName())) {
                merge(c, cs[i], fire);
            } else {
            	object.removeChild(c);
            	object.addChild(cs[i].copy());
            }
			map.remove(c.getPathPart()); 
        }
        if(!map.isEmpty()) {
			Iterator<XModelObject> it = map.values().iterator();
			while(it.hasNext()) {
				XModelObject o = (XModelObject)it.next();
				XChild childEntity = object.getModelEntity().getChild(o.getModelEntity().getName());
				if(childEntity != null && childEntity.isRequired()) {
					continue;
				} 
				o.removeFromParent();
			}
        }
        boolean doFire = false;
		for (int i = 0; i < cs.length; i++) {
			XModelObject c = object.getChildByPath(cs[i].getPathPart());
			if(c == null) continue;
			int ci = ((RegularObjectImpl)object).getIndexOfChild(c);
			if(ci == i) continue;
			doFire = true;
			((RegularObjectImpl)object).move(ci, i, false);
		}
		if(fire && doFire) ((XModelImpl)object.getModel()).fireStructureChanged(object);
    }
    
	static Map<String,XModelObject> emptyMap = new HashMap<String,XModelObject>();
	 
	private static Map<String,XModelObject> getChildrenForSaveAsMap(XModelObject object) {
		XModelObject[] cs = object.getChildrenForSave();
		if(cs.length == 0) return emptyMap;
		Map<String,XModelObject> map = new HashMap<String,XModelObject>();
		for (int i = 0; i < cs.length; i++) { 
			map.put(cs[i].getPathPart(), cs[i]);
		} 
		return map;
	}
	
	public static Set<String> getChildrenToRemove(Map<String,XModelObject> save, XModelObject update) {
		Iterator<String> it = save.keySet().iterator();
		Set<String> set = new HashSet<String>();
		while(it.hasNext()) {
			String p = it.next();
			if(update.getChildByPath(p) == null) set.add(p); 
		}
		return set;
	}
	
	public static XModelObject findAppropriateChild(Set<String> set, XModelObject updateChild, Map<String,XModelObject> map) {
		String entity = updateChild.getModelEntity().getName();
		Iterator<String> it = set.iterator();
		while(it.hasNext()) {
			String p = it.next();
			XModelObject o = map.get(p);
			if(o == null) continue;
			if(entity.equals(o.getModelEntity().getName())) {
				it.remove();
				map.remove(p);
				return o;
			}
		}
		return null;
	}

}

