/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.lookup;

import net.sourceforge.phpdt.core.compiler.CharOperation;
import net.sourceforge.phpdt.internal.compiler.ast.AnonymousLocalTypeDeclaration;
import net.sourceforge.phpdt.internal.compiler.util.Util;

public final class LocalTypeBinding extends NestedTypeBinding {
	final static char[] LocalTypePrefix = { '$', 'L', 'o', 'c', 'a', 'l', '$' };

	private InnerEmulationDependency[] dependents;

	ArrayBinding[] localArrayBindings; // used to cache array bindings of
										// various dimensions for this local
										// type

	public LocalTypeBinding(ClassScope scope, SourceTypeBinding enclosingType) {
		super(new char[][] { CharOperation.concat(LocalTypePrefix,
				scope.referenceContext.name) }, scope, enclosingType);

		if (this.sourceName == AnonymousLocalTypeDeclaration.ANONYMOUS_EMPTY_NAME)
			this.tagBits |= AnonymousTypeMask;
		else
			this.tagBits |= LocalTypeMask;
	}

	/*
	 * Record a dependency onto a source target type which may be altered by the
	 * end of the innerclass emulation. Later on, we will revisit all its
	 * dependents so as to update them (see updateInnerEmulationDependents()).
	 */

	public void addInnerEmulationDependent(BlockScope scope,
			boolean wasEnclosingInstanceSupplied) {
		int index;
		if (dependents == null) {
			index = 0;
			dependents = new InnerEmulationDependency[1];
		} else {
			index = dependents.length;
			for (int i = 0; i < index; i++)
				if (dependents[i].scope == scope)
					return; // already stored
			System.arraycopy(dependents, 0,
					(dependents = new InnerEmulationDependency[index + 1]), 0,
					index);
		}
		dependents[index] = new InnerEmulationDependency(scope,
				wasEnclosingInstanceSupplied);
		// System.out.println("Adding dependency: "+ new
		// String(scope.enclosingType().readableName()) + " --> " + new
		// String(this.readableName()));
	}

	/*
	 * Answer the receiver's constant pool name.
	 * 
	 * NOTE: This method should only be used during/after code gen.
	 */

	public char[] constantPoolName() /* java/lang/Object */{
		return constantPoolName;
	}

	ArrayBinding createArrayType(int dimensionCount) {
		if (localArrayBindings == null) {
			localArrayBindings = new ArrayBinding[] { new ArrayBinding(this,
					dimensionCount) };
			return localArrayBindings[0];
		}

		// find the cached array binding for this dimensionCount (if any)
		int length = localArrayBindings.length;
		for (int i = 0; i < length; i++)
			if (localArrayBindings[i].dimensions == dimensionCount)
				return localArrayBindings[i];

		// no matching array
		System.arraycopy(localArrayBindings, 0,
				localArrayBindings = new ArrayBinding[length + 1], 0, length);
		return localArrayBindings[length] = new ArrayBinding(this,
				dimensionCount);
	}

	public char[] readableName() {
		if (isAnonymousType()) {
			if (superInterfaces == NoSuperInterfaces)
				return ("<" + Util.bind("binding.subclass", new String(superclass.readableName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
			else
				return ("<" + Util.bind("binding.implementation", new String(superInterfaces[0].readableName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
		} else if (isMemberType()) {
			return CharOperation.concat(enclosingType().readableName(),
					sourceName, '.');
		} else {
			return sourceName;
		}
	}

	public char[] shortReadableName() {
		if (isAnonymousType()) {
			if (superInterfaces == NoSuperInterfaces)
				return ("<" + Util.bind("binding.subclass", new String(superclass.shortReadableName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
			else
				return ("<" + Util.bind("binding.implementation", new String(superInterfaces[0].shortReadableName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
		} else if (isMemberType()) {
			return CharOperation.concat(enclosingType().shortReadableName(),
					sourceName, '.');
		} else {
			return sourceName;
		}
	}

	// Record that the type is a local member type
	public void setAsMemberType() {
		tagBits |= MemberTypeMask;
	}

	public void setConstantPoolName(char[] computedConstantPoolName) /* java/lang/Object */{
		this.constantPoolName = computedConstantPoolName;
	}

	public char[] sourceName() {
		if (isAnonymousType()) {
			// return readableName();
			if (superInterfaces == NoSuperInterfaces)
				return ("<" + Util.bind("binding.subclass", new String(superclass.sourceName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$
			else
				return ("<" + Util.bind("binding.implementation", new String(superInterfaces[0].sourceName())) + ">").toCharArray(); //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-1$

		} else
			return sourceName;
	}

	public String toString() {
		if (isAnonymousType())
			return "Anonymous type : " + super.toString(); //$NON-NLS-1$
		if (isMemberType())
			return "Local member type : " + new String(sourceName()) + " " + super.toString(); //$NON-NLS-2$ //$NON-NLS-1$
		return "Local type : " + new String(sourceName()) + " " + super.toString(); //$NON-NLS-2$ //$NON-NLS-1$
	}

	/*
	 * Trigger the dependency mechanism forcing the innerclass emulation to be
	 * propagated to all dependent source types.
	 */

	public void updateInnerEmulationDependents() {
		if (dependents != null) {
			for (int i = 0; i < dependents.length; i++) {
				InnerEmulationDependency dependency = dependents[i];
				// System.out.println("Updating " + new
				// String(this.readableName()) + " --> " + new
				// String(dependency.scope.enclosingType().readableName()));
				dependency.scope.propagateInnerEmulation(this,
						dependency.wasEnclosingInstanceSupplied);
			}
		}
	}
}
