/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.debug.core.model;

import java.text.MessageFormat;

import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.model.IValue;
import org.eclipse.jdi.internal.ValueImpl;
import org.eclipse.jdi.internal.VirtualMachineImpl;

import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;

/**
 * Common functionality for variables that support value modification
 */
public abstract class JDIModificationVariable extends JDIVariable {

	public JDIModificationVariable(JDIDebugTarget target) {
		super(target);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.eclipse.debug.core.model.IValueModification#supportsValueModification
	 * ()
	 */
	@Override
	public boolean supportsValueModification() {
		return true;
	}

	/**
	 * Returns a value generated by the vm from the given expression
	 *
	 * @param expression
	 *            the expression to produce a value from
	 * @return generated value, <code>null</code> is a valid value
	 * @throws DebugException
	 *             if there is a problem generating the value
	 */
	protected Value generateVMValue(String expression) throws DebugException {
		VirtualMachine vm = getVM();
		if (vm == null) {
			requestFailed(
					JDIDebugModelMessages.JDIModificationVariable_Unable_to_generate_value___VM_disconnected__1,
					new VMDisconnectedException());
		}
		String signature = getJavaType().getSignature();
		Value vmValue = null;
		try {
			switch (signature.charAt(0)) {
			case 'Z':
				String flse = Boolean.FALSE.toString();
				String tre = Boolean.TRUE.toString();
				if (expression.equals(tre) || expression.equals(flse)) {
					boolean booleanValue = Boolean.parseBoolean(expression);
					vmValue = vm.mirrorOf(booleanValue);
				}
				break;
			case 'B':
				byte byteValue = Byte.parseByte(expression);
				vmValue = vm.mirrorOf(byteValue);
				break;
			case 'C':
				if (expression.length() == 1) {
					char charValue = expression.charAt(0);
					vmValue = vm.mirrorOf(charValue);
				} else if (expression.length() == 2) {
					char charValue;
					if (!(expression.charAt(0) == '\\')) {
						return null;
					}
					switch (expression.charAt(1)) {
					case 'b':
						charValue = '\b';
						break;
					case 'f':
						charValue = '\f';
						break;
					case 'n':
						charValue = '\n';
						break;
					case 'r':
						charValue = '\r';
						break;
					case 't':
						charValue = '\t';
						break;
					case '\'':
						charValue = '\'';
						break;
					case '\"':
						charValue = '\"';
						break;
					case '\\':
						charValue = '\\';
						break;
					default:
						return null;
					}
					vmValue = vm.mirrorOf(charValue);
				}
				break;
			case 'S':
				short shortValue = Short.parseShort(expression);
				vmValue = vm.mirrorOf(shortValue);
				break;
			case 'I':
				int intValue = Integer.parseInt(expression);
				vmValue = vm.mirrorOf(intValue);
				break;
			case 'J':
				long longValue = Long.parseLong(expression);
				vmValue = vm.mirrorOf(longValue);
				break;
			case 'F':
				float floatValue = Float.parseFloat(expression);
				vmValue = vm.mirrorOf(floatValue);
				break;
			case 'D':
				double doubleValue = Double.parseDouble(expression);
				vmValue = vm.mirrorOf(doubleValue);
				break;
			case 'L':
				if (expression.equals("null")) { //$NON-NLS-1$
					vmValue = null;
				} else if (expression.equals("\"null\"")) { //$NON-NLS-1$
					vmValue = vm.mirrorOf("null"); //$NON-NLS-1$
				} else {
					vmValue = vm.mirrorOf(expression);
				}
				break;

			}
		} catch (NumberFormatException nfe) {
			requestFailed(MessageFormat.format(
					JDIDebugModelMessages.JDIModificationVariable_0,
					getJavaType().toString()), nfe);
		}
		return vmValue;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.eclipse.debug.core.model.IValueModification#verifyValue(java.lang
	 * .String)
	 */
	@Override
	public boolean verifyValue(String expression) throws DebugException {
		Value vmValue = generateVMValue(expression);
		IValue value = JDIValue.createValue(getJavaDebugTarget(), vmValue);
		return verifyValue(value);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.eclipse.debug.core.model.IValueModification#verifyValue(org.eclipse
	 * .debug.core.model.IValue)
	 */
	@Override
	public boolean verifyValue(IValue value) throws DebugException {
		if (value instanceof JDIValue
				&& value.getDebugTarget().equals(getDebugTarget())) {
			JDIValue jv = (JDIValue) value;
			try {
				ValueImpl.checkValue(jv.getUnderlyingValue(),
						getUnderlyingType(), (VirtualMachineImpl) getVM());
				return true;
			} catch (InvalidTypeException e) {
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.eclipse.debug.core.model.IValueModification#setValue(java.lang.String
	 * )
	 */
	@Override
	public final void setValue(String expression) throws DebugException {
		Value value = generateVMValue(expression);
		setJDIValue(value);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.eclipse.jdt.internal.debug.core.model.JDIVariable#setValue(org.eclipse
	 * .debug.core.model.IValue)
	 */
	@Override
	public final void setValue(IValue v) throws DebugException {
		if (v instanceof JDIValue) {
			JDIValue value = (JDIValue) v;
			setJDIValue(value.getUnderlyingValue());
		}
	}

	/**
	 * Set this variable's value to the given value
	 */
	protected abstract void setJDIValue(Value value) throws DebugException;

}
