/*******************************************************************************
 * Copyright (c) 2007 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.jdt.internal.debug.core.logicalstructures;

import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IVariable;
import org.eclipse.jdt.debug.core.IJavaArrayType;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaType;
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.internal.debug.core.HeapWalkingManager;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
import org.eclipse.jdt.internal.debug.core.model.JDIArrayValue;
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
import org.eclipse.jdt.internal.debug.core.model.JDIPlaceholderValue;
import org.eclipse.jdt.internal.debug.core.model.JDIReferenceType;

import com.ibm.icu.text.MessageFormat;

/**
 * Java value containing an array of java objects.  This value is used to hold a list of 
 * all instances of a specific java type.
 * 
 * @since 3.3
 * @see org.eclipse.jdt.internal.debug.ui.heapwalking.AllInstancesActionDelegate
 */
public class JDIAllInstancesValue extends JDIArrayValue {

	private IJavaObject[] fInstances;
	private JDIReferenceType fRoot;
	private IJavaArrayType fType;
	private boolean fIsMoreThanPreference;
	
	/**
	 * Constructor, specifies whether there are more instances available than should be
	 * displayed according to the user's preference settings.
	 * 
	 * @param target the target VM
	 * @param root the root object to get instances for
	 */
	public JDIAllInstancesValue(JDIDebugTarget target, JDIReferenceType root) {
		super(target, null);
		fRoot = root;
		try {
			IJavaType[] javaTypes = target.getJavaTypes("java.lang.Object[]"); //$NON-NLS-1$
			if (javaTypes.length > 0) {
				fType = (IJavaArrayType) javaTypes[0];
			}
		} catch (DebugException e) {}
	}
	
	/**
	 * @return an array of java objects that are instances of the root type
	 */
	protected IJavaObject[] getInstances(){
		if (fInstances != null){
			return fInstances;
		} else {
			IJavaObject[] instances = new IJavaObject[0];
			fIsMoreThanPreference = false;
			if (fRoot != null){
				int max = HeapWalkingManager.getDefault().getAllInstancesMaxCount();
				try{
					if (max == 0){
						instances = fRoot.getInstances(max);
					} else {
						instances = fRoot.getInstances(max+1);
						if (instances.length > max){
							instances[max] = new JDIPlaceholderValue((JDIDebugTarget)fRoot.getDebugTarget(),MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_2,new String[]{Integer.toString(max)}));
							fIsMoreThanPreference = true;
						}
					}
				} catch (DebugException e) {
					JDIDebugPlugin.log(e);
				}
			}
			fInstances = instances;
			return instances;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getLength()
	 */
	public synchronized int getLength() throws DebugException {
		return getInstances().length;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getSize()
	 */
	public int getSize() throws DebugException {
		return getInstances().length;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getValue(int)
	 */
	public IJavaValue getValue(int index) throws DebugException {
		if(index > getInstances().length-1 || index < 0) {
			internalError(LogicalStructuresMessages.JDIAllInstancesValue_0);
		}
		return getInstances()[index];
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getValues()
	 */
	public IJavaValue[] getValues() throws DebugException {
		return getInstances();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getVariable(int)
	 */
	public IVariable getVariable(int offset) throws DebugException {
		if(offset > getInstances().length-1 || offset < 0) {
			internalError(LogicalStructuresMessages.JDIAllInstancesValue_1);
		}
		if(isMoreThanPreference() && offset == getInstances().length-1){
			return new JDIPlaceholderVariable(LogicalStructuresMessages.JDIAllInstancesValue_4, getInstances()[offset]);
		} else {
			return new JDIPlaceholderVariable(MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_5,new String[]{Integer.toString(offset)}), getInstances()[offset]);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getVariables(int, int)
	 */
	public IVariable[] getVariables(int offset, int length) throws DebugException {
		if (length == 0){
			return new IVariable[0];
		}
		if(offset > getInstances().length-1 || offset < 0) {
			internalError(LogicalStructuresMessages.JDIAllInstancesValue_1);
		}
		IVariable[] vars = new JDIPlaceholderVariable[length];
		for (int i = 0; i < length; i++) {
			vars[i] = getVariable(i + offset);
		}
		return vars;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getVariables()
	 */
	public IVariable[] getVariables() throws DebugException {
		return getVariables(0, getInstances().length);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIObjectValue#getReferringObjects(long)
	 */
	public IJavaObject[] getReferringObjects(long max) throws DebugException {
		return new IJavaObject[0];
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#isAllocated()
	 */
	public boolean isAllocated() throws DebugException {
		return getJavaDebugTarget().isAvailable();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#getInitialOffset()
	 */
	public int getInitialOffset() {
		return 0;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIArrayValue#hasVariables()
	 */
	public boolean hasVariables() throws DebugException {
		return getInstances().length > 0;
	}	
	
	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getJavaType()
	 */
	public IJavaType getJavaType() throws DebugException {
		return fType;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getSignature()
	 */
	public String getSignature() throws DebugException {
		return fType.getSignature();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIObjectValue#getReferenceTypeName()
	 */
	public String getReferenceTypeName() throws DebugException {
		return fType.getName();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.internal.debug.core.model.JDIValue#getValueString()
	 */
	public String getValueString() throws DebugException {
		if (isMoreThanPreference()){
			return MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_7,new String[]{Integer.toString(getInstances().length-1)});
		} else if (getInstances().length == 1) {
			return MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_8,new String[]{Integer.toString(getInstances().length)});
		} else {
			return MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_9,new String[]{Integer.toString(getInstances().length)});
		}
	}
	
	/**
	 * Returns a string representation of this value intended to be displayed
	 * in the detail pane of views.  Lists the references on separate lines.
	 * 
	 * @return a string representation of this value to display in the detail pane
	 */
	public String getDetailString(){
		StringBuffer buf = new StringBuffer();
		Object[] elements = getInstances();
		if (elements.length == 0){
			buf.append(LogicalStructuresMessages.JDIAllInstancesValue_10);
		}
		else{
			String length = null;
			if (isMoreThanPreference()){
				length = MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_11,new String[]{Integer.toString(elements.length-1)});
			} else {
				length = Integer.toString(elements.length);
			}
			if (elements.length == 1){
				buf.append(MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_12,new String[]{length}));
			} else {
				buf.append(MessageFormat.format(LogicalStructuresMessages.JDIAllInstancesValue_13,new String[]{length}));
			}
			for (int i = 0; i < elements.length; i++) {
				buf.append(elements[i] + "\n");  //$NON-NLS-1$
			}	
		}
		return buf.toString();
	}
	
	/**
	 * @return whether there are more instances available than should be displayed 
	 */
	protected boolean isMoreThanPreference() {
		getInstances(); //The instances must be requested to know if there are more than the preference
		return fIsMoreThanPreference;
	}

}
