/*******************************************************************************
 * Copyright (c) 2001, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.wst.wsdl.ui.internal.actions;

import java.util.HashMap;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames;
import org.eclipse.wst.wsdl.Definition;
import org.eclipse.wst.wsdl.WSDLElement;
import org.eclipse.wst.wsdl.ui.internal.Messages;
import org.eclipse.wst.wsdl.ui.internal.WSDLEditorPlugin;
import org.eclipse.wst.wsdl.ui.internal.dialogs.NewComponentDialog;
import org.eclipse.wst.wsdl.ui.internal.util.WSDLEditorUtil;
import org.eclipse.wst.wsdl.util.WSDLConstants;
import org.eclipse.wst.xml.core.internal.XMLCorePlugin;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.provisional.format.FormatProcessorXML;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


public class AddElementAction extends BaseNodeAction {
	protected Node parentNode;
	protected String prefix;
	protected String nodeName;
	protected Element newElement;
	protected Node relativeNode = null;
	protected IEditorPart editorPart;
	protected Definition definition;
	protected Document document;
	protected boolean computeTopLevelRefChild;

	protected boolean selectNewlyCreatedObject = true; // We should not be
														// selecting the
														// object in the
														// action..... TODO

	public AddElementAction(String text, String imageDescriptorKey, Node parentNode, String nodeName) {
		setText(text);
		setImageDescriptor(WSDLEditorPlugin.getImageDescriptor(imageDescriptorKey));
		this.parentNode = parentNode;
		this.nodeName = nodeName;
	}

	public AddElementAction(String text, String imageDescriptorKey, Node parentNode, String prefix, String localName) {
		setText(text);
		setImageDescriptor(WSDLEditorPlugin.getImageDescriptor(imageDescriptorKey));
		this.parentNode = parentNode;
		this.prefix = prefix;
		this.nodeName = localName;
	}

	public AddElementAction(String text, Node parentNode, String prefix, String localName) {
		setText(text);
		setImageDescriptor(null);
		this.parentNode = parentNode;
		this.prefix = prefix;
		this.nodeName = localName;
	}

	public AddElementAction(Node parentNode, String prefix, String localName, Node relativeNode) {
		this.parentNode = parentNode;
		this.prefix = prefix;
		this.nodeName = localName;
		this.relativeNode = relativeNode;
	}

	public void setComputeTopLevelRefChild(boolean isEnabled) {
		computeTopLevelRefChild = isEnabled;
	}

	protected void setEditorPart(IEditorPart editorPart) {
		this.editorPart = editorPart;
	}

	protected boolean showDialog() {
		return true;
	}

	public void run() {
		boolean ok = showDialog();
		if (ok) {
			beginRecording();
			performAddElement();
			endRecording();
		}
	}

	protected void performAddElement() {
		if (parentNode != null) {
			newElement = createElement(nodeName);
			addAttributes(newElement);
			if (relativeNode == null && computeTopLevelRefChild) {
				relativeNode = computeTopLevelRefChild(newElement);
			}

			if (relativeNode == null) {
				parentNode.appendChild(newElement);
			}
			else {
				parentNode.insertBefore(newElement, relativeNode);
			}
			// format(parentNode);
			format(newElement);
			// Ugly..... We should not be selecting the object in the graph
			// view in the Action.
			// This should be refactored out. We add this boolean check
			// because WSDLSetTypeDialog.java
			// uses AddImportAction.java but we should not be selecting the
			// newly created import....
			if (selectNewlyCreatedObject) {
				selectObjectForNewElement();
			}
		}
	}

	// Ugly..... We should not be selecting the object in the graph view in
	// the Action.
	// This should be refactored out. We add this boolean check because
	// WSDLSetTypeDialog.java
	// uses AddImportAction.java but we should not be selecting the newly
	// created import....
	// We should call this method before calling run()
	public void selectObjectForNewElement(boolean select) {
		selectNewlyCreatedObject = select;
	}

	public Node getNode() {
		if (parentNode != null) {
			return parentNode;
		}
		else {
			return document;
		}
	}


	public String getUndoDescription() {
		return Messages._UI_ACTION_ADD; //$NON-NLS-1$
	}


	protected Element createElement(String nodeName) {
		Document document = parentNode.getOwnerDocument();

		Element element = (prefix != null && prefix.length() > 0) ? document.createElement(prefix + ":" + nodeName) : document.createElement(nodeName); //$NON-NLS-1$

		return element;
	}

	protected void addAttributes(Element newElement) {
	}


	protected void format(Node parentNode) {
		if (parentNode instanceof IDOMNode) {
			// format selected node
			FormatProcessorXML formatProcessorXML = new FormatProcessorXML();
			formatProcessorXML.formatNode((IDOMNode) parentNode);

		}
	}

	protected Element getDefinitionElement(Element parentElement) {
		Element definitionElement = null;

		for (Node node = parentElement.getOwnerDocument().getFirstChild(); node != null; node = node.getNextSibling()) {
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				Element element = (Element) node;
				if (WSDLEditorUtil.getInstance().getWSDLType(element) == WSDLConstants.DEFINITION) {
					definitionElement = element;
					break;
				}
			}
		}
		return definitionElement;
	}

	public Element getNewElement() {
		return newElement;
	}

	public void setDefinition(Definition definition) {
		this.definition = definition;
	}

	public void selectObjectForNewElement() {
		if (editorPart == null) {
			editorPart = WSDLEditorPlugin.getInstance().getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		}
		if (editorPart != null && definition != null) {
			Object object = WSDLEditorUtil.getInstance().findModelObjectForElement(definition, newElement);
			if (object != null) {
				ISelectionProvider selectionProvider = (ISelectionProvider) editorPart.getAdapter(ISelectionProvider.class);
				if (selectionProvider != null) {
					selectionProvider.setSelection(new StructuredSelection(object));
				}
			}
		}
	}

	public void selectObject(WSDLElement object) {
		if (editorPart == null) {
			editorPart = WSDLEditorPlugin.getInstance().getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor();
		}
		if (editorPart != null && definition != null) {
			if (object != null) {
				ISelectionProvider selectionProvider = (ISelectionProvider) editorPart.getAdapter(ISelectionProvider.class);
				if (selectionProvider != null) {
					selectionProvider.setSelection(new StructuredSelection(object));
				}
			}
		}
	}

	public String showDialogHelper(String title, String defaultName, List usedNames) {
		String result = defaultName;
		NewComponentDialog dialog = new NewComponentDialog(WSDLEditorPlugin.getShell(), title, defaultName, usedNames);
		int rc = dialog.createAndOpen();
		if (rc == IDialogConstants.OK_ID) {
			result = dialog.getName();
		}
		else {
			result = null;
		}
		return result;
	}

	protected Node computeTopLevelRefChild(Node nodeToAdd) {
		Node result = null;
		int a = getPrecedence(nodeToAdd);

		for (Node node = parentNode.getFirstChild(); node != null; node = node.getNextSibling()) {
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				int b = getPrecedence(node);
				if (b > a) {
					result = node;
					break;
				}
			}
		}
		return result;
	}

	protected void createDefinitionStub() {
		if (document != null) {
			// Create the Definitions element with proper namespace
			Preferences preference = XMLCorePlugin.getDefault().getPluginPreferences();
			String charSet = preference.getString(CommonEncodingPreferenceNames.OUTPUT_CODESET);
			if (charSet == null || charSet.trim().equals("")) { //$NON-NLS-1$
				charSet = "UTF-8"; //$NON-NLS-1$
			}
			document.appendChild(document.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"" + charSet + "\"")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			Element root = document.createElement("wsdl:definitions"); //$NON-NLS-1$
			document.appendChild(root);

			// Add various namespace attributes here.
			root.setAttribute("xmlns:soap", "http://schemas.xmlsoap.org/wsdl/soap/"); //$NON-NLS-1$ //$NON-NLS-2$
			root.setAttribute("xmlns:tns", getDefaultNamespace()); //$NON-NLS-1$
			root.setAttribute("xmlns:wsdl", "http://schemas.xmlsoap.org/wsdl/"); //$NON-NLS-1$ //$NON-NLS-2$
			root.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); //$NON-NLS-1$ //$NON-NLS-2$
			root.setAttribute("name", getFileName()); //$NON-NLS-1$
			root.setAttribute("targetNamespace", getDefaultNamespace()); //$NON-NLS-1$

			definition.setElement(root);
			parentNode = root;
			prefix = definition.getPrefix(WSDLConstants.WSDL_NAMESPACE_URI);
		}
	}

	private String getDefaultNamespace() {
		String namespace = WSDLEditorPlugin.getInstance().getPreferenceStore().getString(WSDLEditorPlugin.DEFAULT_TARGET_NAMESPACE_ID); 
		if (!namespace.endsWith("/")) { //$NON-NLS-1$
			namespace = namespace.concat("/"); //$NON-NLS-1$
		}

		namespace += getFileName() + "/"; //$NON-NLS-1$

		return namespace;
	}

	private String getFileName() {
		String fileLocation = definition.getLocation();
		IPath filePath = new Path(fileLocation);
		return filePath.removeFileExtension().lastSegment().toString();
	}

	protected static HashMap precedenceMap = createPrecedenceMap();

	protected static int getPrecedence(Node node) {
		int result = 2;
		String localName = node.getLocalName();
		if (localName != null) {
			Integer integer = (Integer) precedenceMap.get(localName);
			if (integer != null) {
				result = integer.intValue();
			}
		}
		return result;
	}

	protected static HashMap createPrecedenceMap() {
		HashMap hashMap = new HashMap();
		hashMap.put(WSDLConstants.DOCUMENTATION_ELEMENT_TAG, new Integer(1));
		hashMap.put(WSDLConstants.IMPORT_ELEMENT_TAG, new Integer(3));
		hashMap.put(WSDLConstants.TYPES_ELEMENT_TAG, new Integer(4));
		hashMap.put(WSDLConstants.MESSAGE_ELEMENT_TAG, new Integer(5));
		hashMap.put(WSDLConstants.PORT_TYPE_ELEMENT_TAG, new Integer(6));
		hashMap.put(WSDLConstants.BINDING_ELEMENT_TAG, new Integer(7));
		hashMap.put(WSDLConstants.SERVICE_ELEMENT_TAG, new Integer(8));
		return hashMap;
	}
}