/*******************************************************************************
 * Copyright (c) 2000, 2009 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.jsdt.internal.core.dom.rewrite;

import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;

import org.eclipse.wst.jsdt.core.dom.AST;
import org.eclipse.wst.jsdt.core.dom.ASTNode;
import org.eclipse.wst.jsdt.core.dom.Block;
import org.eclipse.wst.jsdt.core.dom.FieldDeclaration;
import org.eclipse.wst.jsdt.core.dom.Modifier;
import org.eclipse.wst.jsdt.core.dom.TryStatement;
import org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression;
import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement;
import org.eclipse.wst.jsdt.internal.core.dom.rewrite.RewriteEventStore.CopySourceInfo;

/**
 *
 */
public final class NodeInfoStore {
	private AST ast;

	private Map placeholderNodes;
	private Set collapsedNodes;

	public NodeInfoStore(AST ast) {
		super();
		this.ast= ast;
		this.placeholderNodes= null;
		this.collapsedNodes= null;
	}

	/**
	 * Marks a node as a placehoder for a plain string content. The type of the node should correspond to the
	 * code's code content.
	 * @param placeholder The placeholder node that acts for the string content.
	 * @param code The string content.
	 */
	public final void markAsStringPlaceholder(ASTNode placeholder, String code) {
		StringPlaceholderData data= new StringPlaceholderData();
		data.code= code;
		setPlaceholderData(placeholder, data);
	}

	/**
	 * Marks a node as a copy or move target. The copy target represents a copied node at the target (copied) site.
	 * @param target The node at the target site. Can be a placeholder node but also the source node itself.
	 * @param copySource The info at the source site.
	 */
	public final void markAsCopyTarget(ASTNode target, CopySourceInfo copySource) {
		CopyPlaceholderData data= new CopyPlaceholderData();
		data.copySource= copySource;
		setPlaceholderData(target, data);
	}

	/**
	 * Creates a placeholder node of the given type. <code>null</code> if the type is not supported
	 * @param nodeType Type of the node to create. Use the type constants in {@link NodeInfoStore}.
	 * @return Returns a place holder node.
	 */
	public final ASTNode newPlaceholderNode(int nodeType) {
	    try {
		    ASTNode node= this.ast.createInstance(nodeType);
		    switch (node.getNodeType()) {
				case ASTNode.FIELD_DECLARATION:
				    ((FieldDeclaration) node).fragments().add(this.ast.newVariableDeclarationFragment());
				    break;
				case ASTNode.MODIFIER:
				    ((Modifier) node).setKeyword(Modifier.ModifierKeyword.ABSTRACT_KEYWORD);
					break;
				case ASTNode.TRY_STATEMENT :
					((TryStatement) node).setFinally(this.ast.newBlock()); // have to set at least a finally block to be legal code
					break;
				case ASTNode.VARIABLE_DECLARATION_EXPRESSION :
				    ((VariableDeclarationExpression) node).fragments().add(this.ast.newVariableDeclarationFragment());
			    	break;
				case ASTNode.VARIABLE_DECLARATION_STATEMENT :
				    ((VariableDeclarationStatement) node).fragments().add(this.ast.newVariableDeclarationFragment());
		    		break;
			}
		    return node;
	    } catch (IllegalArgumentException e) {
	        return null;
	    }
 	}


	// collapsed nodes: in source: use one node that represents many; to be used as
	// copy/move source or to replace at once.
	// in the target: one block node that is not flattened.

	public Block createCollapsePlaceholder() {
		Block placeHolder= this.ast.newBlock();
		if (this.collapsedNodes == null) {
			this.collapsedNodes= new HashSet();
		}
		this.collapsedNodes.add(placeHolder);
		return placeHolder;
	}

	public boolean isCollapsed(ASTNode node) {
		if (this.collapsedNodes != null) {
			return this.collapsedNodes.contains(node);
		}
		return false;
	}

	public Object getPlaceholderData(ASTNode node) {
		if (this.placeholderNodes != null) {
			return this.placeholderNodes.get(node);
		}
		return null;
	}

	private void setPlaceholderData(ASTNode node, PlaceholderData data) {
		if (this.placeholderNodes == null) {
			this.placeholderNodes= new IdentityHashMap();
		}
		this.placeholderNodes.put(node, data);
	}

	static class PlaceholderData {
		// base class
	}

	protected static final class CopyPlaceholderData extends PlaceholderData {
		public CopySourceInfo copySource;
		public String toString() {
			return "[placeholder " + this.copySource +"]";  //$NON-NLS-1$//$NON-NLS-2$
		}
	}

	protected static final class StringPlaceholderData extends PlaceholderData {
		public String code;
		public String toString() {
			return "[placeholder string: " + this.code +"]"; //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	/**
	 *
	 */
	public void clear() {
		this.placeholderNodes= null;
		this.collapsedNodes= null;
	}
}
