/*******************************************************************************
 * 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;

import java.util.*;

import org.jboss.tools.common.model.*;
import org.eclipse.swt.widgets.Display;
import org.jboss.tools.common.meta.XMapping;
import org.jboss.tools.common.meta.action.*;
import org.jboss.tools.common.meta.action.impl.handlers.*;
import org.jboss.tools.common.meta.help.HelpUtil;
import org.jboss.tools.common.meta.key.*;

public abstract class SpecialWizardSupport {
    public static final String OK = Messages.SpecialWizardSupport_OK;
    public static final String CANCEL = Messages.SpecialWizardSupport_Cancel;
    public static final String BACK = Messages.SpecialWizardSupport_BackArrow;
    public static final String NEXT = Messages.SpecialWizardSupport_NextArrow;
    public static final String FINISH = Messages.SpecialWizardSupport_Finish;
	public static final String HELP = Messages.SpecialWizardSupport_Help;
	public static final String CLOSE = Messages.SpecialWizardSupport_Close;
	
	static final Set<String> standardButtons = new HashSet<String>();
	
	static {
		standardButtons.add(OK);
		standardButtons.add(CANCEL);
		standardButtons.add(BACK);
		standardButtons.add(NEXT);
		standardButtons.add(FINISH);
		standardButtons.add(HELP);
		standardButtons.add(CLOSE);
	}

    private int stepId = 0;
    private boolean finished = false;
    private XEntityData[] data = null;
    protected XAction action = null;
    protected XModelObject target;
    protected Properties p;
    private SpecialWizardControlListener listener = null;

    public SpecialWizardSupport() {}
    
    public void dispose() {
    	/*
    	if (p!=null) p.clear();
    	p = null;
    	listener = null;
    	*/
    }
    
    public final void setAction(XAction action) {
    	this.action = action;
    }

    public final void setActionData(XAction action, XEntityData[] data, XModelObject target, Properties p) {
        this.action = action;
        this.data = data;
        this.target = target;
        this.p = (p != null) ? p : new Properties();
        stepId = 0;
        finished = false;
        reset();
    }
    
    public void help() throws XModelException {
    	HelpUtil.helpEclipse(getTarget().getModel(), getHelpKey());
    }

    protected void reset() {}

    public final XEntityData[] getEntityData() {
        return data;
    }

    public String getTitle() {
    	String title = WizardKeys.getHeader(getHelpKey());
    	if(title != null) return title;
    	if(getEntityData().length > 0) {
    		title = WizardKeys.getHeader(action, getEntityData()[0].getModelEntity()); // no step id
    	}
    	if(title != null) return title;
		String n = (action == null) ? "" : "" + action.getDisplayName(); //$NON-NLS-1$ //$NON-NLS-2$
		if(n.endsWith("...")) n = n.substring(0, n.length() - 3); //$NON-NLS-1$
		return n;
    }
    
    public String getSubtitle() {
    	String key = getHelpKey();
    	String t = WizardKeys.getTitle(key);
    	if(t == null && key != null && key.endsWith("_0")) { //$NON-NLS-1$
    		String key1 = key.substring(0, key.length() - 2);
    		t = WizardKeys.getTitle(key1);
    	}
    	if(t == null && getEntityData().length > 0) {
    		t = WizardKeys.getTitle(action, getEntityData()[0].getModelEntity()); // no step id
    	}
		return t;
    }

    public boolean isEnabled(XModelObject target) {
        return target != null && target.isObjectEditable();
    }

    public String getMessage(int stepId) {
        return WizardKeys.getMessage(getHelpKey());
    }

    /*
     * May return preset or previosly entered data (name-value)
     */
    public String[][] getInfo(int stepId) {
        return new String[0][0];
    }

    /*
     * Returns user friendly display name or message by attribute
     * internal name. If null returned, attrname is used as display name.
     */
    public String getAttributeMessage(int stepId, String attrname) {
        return null;
    }

    public void setControlListener(SpecialWizardControlListener listener) {
        this.listener = listener;
    }

    public void fireCommand(final String command) {
        if(listener != null) {
			Display.getDefault().syncExec( 
				new Runnable() {
					public void run() {
						listener.action(command);
					}
				}
			);
        }
    }

    public int getStepId() {
        return stepId;
    }

    public final void setStepId(int stepId) {
        this.stepId = stepId;
    }

    public final boolean isFinished() {
        return finished;
    }

    public final void setFinished(boolean b) {
        finished = b;
    }

    public abstract void action(String name) throws XModelException;

    public String[] getActionNames(int stepId) {
        return new String[]{OK, CANCEL, HELP};
    }
    
    public boolean canBeProcessedByStandardWizard() {
    	// By default only one-step wizards are accepted;
    	if(data == null || data.length > 1) return false;
    	if(!DEFAULT_IMLEMENTING_CLASS.equals(getStepImplementingClass(0))) {
    		return false;
    	}
    	String[] buttons = getActionNames(0);
    	for (int i = 0; i < buttons.length; i++) {
    		if(!standardButtons.contains(buttons[i])) return false;
    	}
    	return true;
    }

    public String getDefaultActionName(int stepId) {
        String[] actions = getActionNames(stepId);
        if(actions == null || actions.length == 0) return null;
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < actions.length; i++) set.add(actions[i]);
        if(set.contains(OK)) return OK;
        if(set.contains(FINISH)) return FINISH;
        if(set.contains(NEXT)) return NEXT;
        return null;
    }

    private Thread thread = null;

    public final void startThread(Runnable task) {
        if(thread != null) stopThread(true);
        thread = new Thread(task);
        thread.start();
    }

    public final void stopThread(boolean force) {
        if(thread == null) return;
        if(thread.isAlive() && force) {
            	thread.stop();
        }
        thread = null;
    }

    public String getHelpKey() {
    	if(action == null) return null;
    	String key = action.getProperty("key"); //$NON-NLS-1$
    	if(key != null && key.length() > 0) return key;
        return target.getModelEntity().getName() + "_" + action.getName() + "_" + stepId; //$NON-NLS-1$ //$NON-NLS-2$
    }

    public final XModelObject getTarget() {
        return target;
    }

    public final Properties getProperties() {
    	if (p == null) p = new Properties();
        return p;
    }

    public boolean isActionEnabled(String name) {
        return true;
    }
    
    public boolean isFieldEditorEnabled(int stepId, String name, Properties values) {
    	return true;
    }
    
    protected DefaultWizardDataValidator defaultValidator = new DefaultWizardDataValidator();
    
    public WizardDataValidator getValidator(int step) {
		defaultValidator.setSupport(this, step);
		return defaultValidator;    	
    }
    
    static String DEFAULT_IMLEMENTING_CLASS = "org.jboss.tools.common.model.ui.wizards.special.SpecialWizardStep"; //$NON-NLS-1$

    public String getStepImplementingClass(int stepId) {
        return DEFAULT_IMLEMENTING_CLASS;
    }

    // helpers

    public Properties extractStepData(int index) throws RuntimeException {
    	XEntityData d = getEntityData()[index];
    	Properties p = DefaultCreateHandler.getProperties(d);
		XAttributeData[] ads = d.getAttributeData();
		for (int i = 0; i < ads.length; i++) {
			String n = ads[i].getAttribute().getName();
			if(!isFieldEditorEnabled(index, n, p)) continue;
			DefaultCreateHandler.extractProperty(ads[i]);
		}
        return p;
    }
    
    public void initStepData(int index, XModelObject object) {
    	XEntityData d = getEntityData()[index];
        XAttributeData[] ad = d.getAttributeData();
        for (int i = 0; i < ad.length; i++) {
            String n = ad[i].getAttribute().getName();
            String v = object.getAttributeValue(n);
            if(v != null) ad[i].setValue(v);
            v = action.getProperty("attribute." + n); //$NON-NLS-1$
            if(v != null) ad[i].setValue(v);
        }
    }

    public final XAttributeData findAttribute(int index, String name) {
        return HUtil.find(getEntityData(), index, name);
    }

    public final String getAttributeValue(int index, String name) {
        XAttributeData d = findAttribute(index, name);
        return (d != null) ? d.getValue() : null;
    }

    public final void setAttributeValue(int index, String name, String value) {
        XAttributeData d = findAttribute(index, name);
        if(d != null) d.setValue(value);
    }

    public final void setValueList(int index, String name, String[] values) {
        HUtil.hackAttributeConstraintList(getEntityData(), index, name, values);
    }

    public final void setAttributeContext(int index, String name, Object context) {
        XAttributeData d = findAttribute(index, name);
        if(d != null) d.getAttribute().getEditor().setContext(context);
    }
    
    public final void setAttributeDataByObject(int index, XModelObject o) {
		XAttributeData[] ad = getEntityData()[0].getAttributeData();
		for (int i = 0; i < ad.length; i++) {
			String n = ad[i].getAttribute().getName();
			String v = o.getAttributeValue(n);
			if(v != null) setAttributeValue(0, n, v);
		}
    }
    
    protected final void replaceEntityData(XEntityData d, int stepId) {
    	if(data[stepId] == d) return;
    	XAttributeData[] ad = d.getAttributeData();
    	for (int i = 0; i < ad.length; i++) {
    		String n = ad[i].getAttribute().getName();
    		String v = getAttributeValue(stepId, n);
    		if(v == null) v = ad[i].getAttribute().getDefaultValue();
    		if(v != null) ad[i].setValue(v);    		
    	}
    	data[stepId] = d;
    }

	public int getPreviousStepId() {
		return getStepId() - 1;	
	}
	
	public String getFocusAttribute(int stepId) {
		XEntityData[] ds = getEntityData();
		if(ds == null || stepId < 0 || stepId >= ds.length) return null;
		XEntityData d = ds[stepId];
		XAttributeData[] ad = d.getAttributeData();
		if(ad.length == 0) return null;
		XAttributeData best = null;
		int importance = -1;
		for (int i = 0; i < ad.length && importance < 3; i++) {
			int imp = (ad[i].getMandatoryFlag()) ? 1 : 0;
			imp += (ad[i].getValue() == null || ad[i].getValue().length() == 0) ? 2 : 0;
			if(imp > importance) {
				best = ad[i];
				importance = imp;
			}
		}
		return best == null ? null : best.getAttribute().getName();
	}
	
	public String getHelpContextId() {
		if(action == null) return null;
		String s = action.getProperty("helpId"); //$NON-NLS-1$
		if(s == null || s.length() == 0) return null;
		int i = s.indexOf('%');
		if(i < 0) return s;
		int j = s.indexOf('%', i + 1);
		if(j < 0) return s;
		String vr = s.substring(i + 1, j);
		String tail = s.substring(j + 1);
		XMapping map = action.getMetaModel().getMapping("HelpPlugins"); //$NON-NLS-1$
		if(map == null) return s;
		String varValue = map.getValue(vr);
		if(varValue == null) return s;
		return varValue + tail;
	}

}

