/*******************************************************************************
 * 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.meta.action.impl.handlers;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Properties;

import org.jboss.tools.common.meta.XAdoptManager;
import org.jboss.tools.common.meta.XAttribute;
import org.jboss.tools.common.meta.XChild;
import org.jboss.tools.common.meta.XModelEntity;
import org.jboss.tools.common.meta.action.XAttributeData;
import org.jboss.tools.common.meta.action.XEntityData;
import org.jboss.tools.common.meta.action.impl.AbstractHandler;
import org.jboss.tools.common.meta.action.impl.SpecialWizardSupport;
import org.jboss.tools.common.meta.action.impl.XEntityDataImpl;
import org.jboss.tools.common.meta.impl.XMetaDataConstants;
import org.jboss.tools.common.meta.impl.XModelMetaDataImpl;
import org.jboss.tools.common.model.ServiceDialog;
import org.jboss.tools.common.model.XModel;
import org.jboss.tools.common.model.XModelBuffer;
import org.jboss.tools.common.model.XModelException;
import org.jboss.tools.common.model.XModelObjectConstants;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.event.ActionDeclinedException;
import org.jboss.tools.common.model.plugin.ModelMessages;
import org.jboss.tools.common.model.util.XModelObjectLoaderUtil;

public class PasteHandler extends AbstractHandler {	
	/**
	 * When more than one object is pasted and paste of each object
	 * invokes a wizard, to cancel paste of current object and all 
	 * subsequent objects, wizard should set this context property
	 * to 'true'.
	 */	
	public static String IS_CANCELLED = "isCancelled"; //$NON-NLS-1$

    public PasteHandler() {
        data = new XEntityDataImpl[1];
    }

    private static String[] attrs = {"attribute name", XModelObjectConstants.YES, //$NON-NLS-1$
                                     "entity name", XModelObjectConstants.YES, //$NON-NLS-1$
                                     "parameters", XModelObjectConstants.NO}; //$NON-NLS-1$

    public XEntityData[] getEntityData(XModelObject object) {
        return getEntityData(object, 0);
    }

    protected XEntityData[] getEntityData(XModelObject object, int bi) {
        if(getBuffer(object).source(bi) == null) return null;
        ArrayList<String[]> a = new ArrayList<String[]>();
        a.add(new String[]{getEntityName(object, bi), XModelObjectConstants.YES});
        String an = getAttributeName(getEntityName(object, bi));
        if(an != null) a.add(new String[]{an, XModelObjectConstants.YES});
        for (int i = 0; i < attrs.length; i += 2)
          register(object, attrs[i], attrs[i + 1], a, bi);
        String[][] ds = new String[a.size()][];
        for (int i = 0; i < ds.length; i++) ds[i] = (String[])a.get(i);
        data[0] = XEntityDataImpl.create(ds);
        setDefaultData(object, bi);
        return data;
    }

    private void register(XModelObject o, String attr, String mandatory, ArrayList<String[]> list, int bi) {
        if(hasAttribute(o.getModel(), attr, bi)) list.add(new String[]{attr, mandatory});
    }

    public void executeHandler(XModelObject object, Properties prop) throws XModelException {
        if(!isEnabled(object)) return;
        boolean isDrop = (prop != null) && XModelObjectConstants.TRUE.equals(prop.getProperty("isDrop")); //$NON-NLS-1$
        int bs = object.getModel().getModelBuffer().getSize();
        for (int i = 0; i < bs; i++) {
        	execute(object, i, isDrop, prop);
        	if(prop != null && XModelObjectConstants.TRUE.equals(prop.getProperty(IS_CANCELLED))) return;
        } 
    }

    protected void execute(XModelObject parent, int sourceIndex, boolean isDrop, Properties p) throws XModelException {
        XModelBuffer buffer = getBuffer(parent);
        boolean isParent = isParent(parent, buffer.copy(sourceIndex));
        if(isParent) {
			if(!prepaste(parent, sourceIndex)) return;
            if(!isDrop)
              paste(parent, sourceIndex, p);
            else
              pasteOnDrop(parent, sourceIndex, p);
        } else {
            drop(parent, buffer.source(sourceIndex), p);
        }
    }
    
    protected final void paste(XModelObject parent, int sourceIndex, Properties p) throws XModelException {
    	XModelObject source = getBuffer(parent).source(sourceIndex);
        XModelObject copy = getBuffer(parent).copy(sourceIndex);
        XModelObject existing = parent.getChildByPath(copy.getPathPart());
        if(existing != null) {
        	XChild c = parent.getModelEntity().getChild(existing.getModelEntity().getName());
        	if(c != null && c.isRequired() && c.getMaxCount() == 1) return;
        }
        String found = (existing == null) ? null : copy.getModelEntity().getRenderer().getTitle(copy);
        String entity = getEntityName(parent, sourceIndex);
		getEntityData(parent, sourceIndex);
		XAttributeData[] ad = ((XEntityData[])data)[0].getAttributeData();
		boolean mustGenerate = (found != null && mustGenerate(entity));
		if(!mustGenerate) {
			int i = (found == null) ? 0 : PasteEnterNewNameSupport.run(parent, source, copy, data[0]);
			if(i != 0) throw new ActionDeclinedException("Paste declined.");
		}
        for (int j = 0; j < ad.length; j++)
          copy.setAttributeValue(ad[j].getAttribute().getName(), ad[j].getValue());
        if(!mustGenerate) {
        	String an = getAttributeName(getEntityName(parent, sourceIndex));
        	XAttributeData a = (an == null) ? null : HUtil.find(data, 0, an);
        	if(a != null) copy.setAttributeValue(an, a.getValue());
        } else {
        	generate(parent, copy, getAttributeName(entity));
        }
        copy = modify(copy.copy());
        DefaultCreateHandler.addCreatedObject(parent, copy, p);
		if(copy.isActive()) onChildPasted(copy);        
    }
    
	private boolean prepaste(XModelObject parent, int sourceIndex) {
		XModelObject source = parent.getModel().getModelBuffer().source(sourceIndex);
		String entity = source.getModelEntity().getName();
		XChild c = parent.getModelEntity().getChild(entity);
		if(c == null || c.getMaxCount() > 1) return true;  
		XModelObject co = parent.getChildByPath(source.getPathPart());
		if(co == null) return true;
		if(co == source) {
			if(sourceIndex == 0) showUniqueMessae(parent, source);
			return false;
		}
		String n = "<" + co.getModelEntity().getXMLSubPath() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
		String message = MessageFormat.format("Replace existing element {0}?", n);
		ServiceDialog d = parent.getModel().getService();
		int q = d.showDialog("Paste", message, new String[]{ModelMessages.OK, ModelMessages.Cancel}, null, ServiceDialog.QUESTION);
		if(q != 0) return false;
		DefaultRemoveHandler.removeFromParent(co);
		return true;
	}
	private void showUniqueMessae(XModelObject parent, XModelObject source) {
		ServiceDialog d = parent.getModel().getService();
		String message = MessageFormat.format(
				"{0} may have only one {1}. You cannot add its copy.",
				DefaultCreateHandler.title(parent, true), source.getAttributeValue(XModelObjectConstants.ATTR_ELEMENT_TYPE));
		d.showDialog(ModelMessages.WARNING, message, new String[]{SpecialWizardSupport.OK}, null, ServiceDialog.WARNING);
	}

	protected void onChildPasted(XModelObject child) {}

    protected void pasteOnDrop(XModelObject parent, int sourceIndex, Properties p) throws XModelException {
        paste(parent, sourceIndex, p);
    }

    protected final void drop(XModelObject parent, XModelObject source, Properties p) throws XModelException {
        XAdoptManager am = parent.getModelEntity().getAdoptManager();
        if(am != null) am.adopt(parent, source, p);
    }

    protected XModelObject modify(XModelObject c) {
        return c;
    }

    public boolean isEnabled(XModelObject object) {
        XModelBuffer buffer = object.getModel().getModelBuffer();
        int bs = buffer.getSize();
        if(bs == 0 || object == null || !object.isObjectEditable()) return false;
        for (int i = 0; i < bs; i++) {
            XModelObject s = buffer.source(i);
            boolean b = (s != null && (isParent(object, s) || canAdopt(object, s)));
            if(!b) return false;
        }
        return true;
    }

    protected boolean isParent(XModelObject p, XModelObject c) {
        c = c.copy(true, 0);
        return (c != null) && (p.getModelEntity().getChild(c.getModelEntity().getName()) != null);
    }

    protected boolean canAdopt(XModelObject target, XModelObject bullet) {
        XAdoptManager am = target.getModelEntity().getAdoptManager();
        return (am != null && am.isAdoptable(target, bullet));
    }

    protected String getEntityName(XModelObject object, int i) {
        String entity = action.getProperty(XMetaDataConstants.ENTITY);
        if(entity != null) return entity;
        return getBuffer(object).source(i).getModelEntity().getName();
    }

    protected String getAttributeName(String entity) {
    	XModelEntity e = XModelMetaDataImpl.getInstance().getEntity(entity);
    	if(e.getAttribute(XModelObjectLoaderUtil.ATTR_ID_NAME) != null) return XModelObjectLoaderUtil.ATTR_ID_NAME;
    	XAttribute[] as = e.getAttributes();
    	for (int i = 0; i < as.length; i++) 
    		if(XModelObjectConstants.TRUE.equals(as[i].getProperty("id"))) return as[i].getName(); //$NON-NLS-1$
		if(e.getAttribute(XModelObjectConstants.ATTR_NAME) != null) return XModelObjectConstants.ATTR_NAME;
    	return null;
    }
    
    protected boolean mustGenerate(String entity) {
    	XModelEntity e = XModelMetaDataImpl.getInstance().getEntity(entity);
    	if(e.getAttribute(XModelObjectLoaderUtil.ATTR_ID_NAME) != null) return true;
    	XAttribute[] as = e.getAttributes();
    	for (int i = 0; i < as.length; i++) 
    		if(XModelObjectConstants.TRUE.equals(as[i].getProperty("id")) && //$NON-NLS-1$
    		   XModelObjectConstants.TRUE.equals(as[i].getProperty("generate"))) return true; //$NON-NLS-1$
    	return false;
    }
    
    protected int generate(XModelObject parent, XModelObject copy, String attr) {
    	String v = copy.getAttributeValue(attr);
    	while(v.length() > 0 && Character.isDigit(v.charAt(v.length() - 1))) v = v.substring(0, v.length() - 1);
    	int k = 1;
    	String pp = copy.getPathPart();
		while(parent.getChildByPath(pp) != null) {
			copy.setAttributeValue(attr, v + k);
			String ppn = copy.getPathPart();
///			if(ppn.equals(pp)) return 0;
			pp = ppn;
			++k;
		}
    	return 0;
    }

    protected boolean hasAttribute(XModel model, String attr, int bi) {
        return model.getMetaData().getEntity(getEntityName(model.getRoot(), bi)).getAttribute(attr) != null;
    }

    public void setDefaultData(XModelObject object) {
        setDefaultData(object, 0);
    }
    public void setDefaultData(XModelObject object, int bi) {
        if(!isEnabled(object)) return;
        XModelObject c = getBuffer(object).copy(bi);
        XAttributeData[] ad = ((XEntityData[])data)[0].getAttributeData();
        for (int i = 0; i < ad.length; i++)
          ad[i].setValue(c.getAttributeValue(ad[i].getAttribute().getName()));
    }

    public void setData(XEntityData[] data) {}

    private XModelBuffer getBuffer(XModelObject o) {
        return o.getModel().getModelBuffer();
    }

    public boolean isEnabled(XModelObject object, XModelObject[] objects) {
        return (objects == null || objects.length == 1) && isEnabled(object);
    }

}
