/*******************************************************************************
 * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
 * 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:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.report;

import org.objectweb.asm.Type;

/**
 * Names for the Java language.
 */
public class JavaNames implements ILanguageNames {

	public String getPackageName(final String vmname) {
		if (vmname.length() == 0) {
			return "default";
		}
		return vmname.replace('/', '.');
	}

	private String getClassName(final String vmname) {
		final int pos = vmname.lastIndexOf('/');
		final String name = pos == -1 ? vmname : vmname.substring(pos + 1);
		return name.replace('$', '.');
	}

	private boolean isAnonymous(final String vmname) {
		final int dollarPosition = vmname.lastIndexOf('$');
		if (dollarPosition == -1) {
			return false;
		}
		final int internalPosition = dollarPosition + 1;
		if (internalPosition == vmname.length()) {
			// shouldn't happen for classes compiled from Java source
			return false;
		}
		// assume non-identifier start character for anonymous classes
		final char start = vmname.charAt(internalPosition);
		return !Character.isJavaIdentifierStart(start);
	}

	public String getClassName(final String vmname, final String vmsignature,
			final String vmsuperclass, final String[] vminterfaces) {
		if (isAnonymous(vmname)) {
			final String vmsupertype;
			if (vminterfaces != null && vminterfaces.length > 0) {
				vmsupertype = vminterfaces[0];
			} else if (vmsuperclass != null) {
				vmsupertype = vmsuperclass;
			} else {
				vmsupertype = null;
			}
			// append Eclipse style label, e.g. "Foo.new Bar() {...}"
			if (vmsupertype != null) {
				final StringBuilder builder = new StringBuilder();
				final String vmenclosing = vmname.substring(0,
						vmname.lastIndexOf('$'));
				builder.append(getClassName(vmenclosing)).append(".new ")
						.append(getClassName(vmsupertype)).append("() {...}");
				return builder.toString();
			}
		}
		return getClassName(vmname);
	}

	public String getQualifiedClassName(final String vmname) {
		return vmname.replace('/', '.').replace('$', '.');
	}

	public String getMethodName(final String vmclassname,
			final String vmmethodname, final String vmdesc,
			final String vmsignature) {
		return getMethodName(vmclassname, vmmethodname, vmdesc, false);
	}

	public String getQualifiedMethodName(final String vmclassname,
			final String vmmethodname, final String vmdesc,
			final String vmsignature) {
		return getQualifiedClassName(vmclassname) + "."
				+ getMethodName(vmclassname, vmmethodname, vmdesc, true);
	}

	private String getMethodName(final String vmclassname,
			final String vmmethodname, final String vmdesc,
			final boolean qualifiedParams) {
		if ("<clinit>".equals(vmmethodname)) {
			return "static {...}";
		}
		final StringBuilder result = new StringBuilder();
		if ("<init>".equals(vmmethodname)) {
			if (isAnonymous(vmclassname)) {
				return "{...}";
			} else {
				result.append(getClassName(vmclassname));
			}
		} else {
			result.append(vmmethodname);
		}
		result.append('(');
		final Type[] arguments = Type.getArgumentTypes(vmdesc);
		boolean comma = false;
		for (final Type arg : arguments) {
			if (comma) {
				result.append(", ");
			} else {
				comma = true;
			}
			if (qualifiedParams) {
				result.append(getQualifiedClassName(arg.getClassName()));
			} else {
				result.append(getShortTypeName(arg));
			}
		}
		result.append(')');
		return result.toString();
	}

	private String getShortTypeName(final Type type) {
		final String name = type.getClassName();
		final int pos = name.lastIndexOf('.');
		final String shortName = pos == -1 ? name : name.substring(pos + 1);
		return shortName.replace('$', '.');
	}

}
