/*
 * Copyright IBM Corp. and others 2004
 *
 * 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/
 * or the Apache License, Version 2.0 which accompanies this distribution and
 * is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following
 * Secondary Licenses when the conditions for such availability set
 * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
 * General Public License, version 2 with the GNU Classpath
 * Exception [1] and GNU General Public License, version 2 with the
 * OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 */
package com.ibm.oti.VMCPTool;

import java.io.PrintWriter;
import java.util.Map;

import org.w3c.dom.Element;

public class VirtualMethodRef extends PrimaryItem implements Constants {

	private static class Alias extends PrimaryItem.AliasWithClass {
		final NameAndSignature nas;

		Alias(VersionRange[] versions, String[] flags, ClassRef classRef, NameAndSignature nas) {
			super(versions, flags, classRef);
			this.nas = nas;
		}

		void writeSecondaryItems(ConstantPoolStream ds) {
			if (checkClassForWriteSecondary(ds)) {
				ds.writeSecondaryItem(nas);
			}
		}

		void write(ConstantPoolStream ds) {
			if (checkClassForWrite(ds)) {
				ds.alignTo(4);
				ds.markVirtualMethod();
				ds.writeInt(ds.getIndex(classRef));
				ds.writeInt(ds.getOffset(nas) - ds.getOffset());
			}
		}

	}

	private static class Factory implements Alias.Factory {
		private final Map<String, ClassRef> classes;
		private ClassRef classRef;

		Factory(Map<String, ClassRef> classes) {
			this.classes = classes;
			this.classRef = null;
		}

		public PrimaryItem.Alias alias(Element e, PrimaryItem.Alias proto) {
			Alias p = (Alias) proto;
			return new Alias(
					versions(e, p),
					flags(e, p),
					classRef(e),
					new NameAndSignature(
							attribute(e, "name", p != null ? p.nas.name.data : ""),
							attribute(e, "signature", p != null ? p.nas.signature.data : "")));
		}

		private ClassRef classRef(Element e) {
			String name = attribute(e, "class", null);
			if (name == null) {
				return classRef;
			}
			if (classRef == null) {
				classRef = classes.get(name);
			}
			return classes.get(name);
		}
	}

	public VirtualMethodRef(Element e, Map<String, ClassRef> classes) {
		super(e, METHODALIAS, new Factory(classes));
	}

	protected String cMacroName() {
		String methodName = ((Alias) primary).nas.name.data.toUpperCase();
		if (methodName.indexOf('<') != -1) {
			StringBuilder newName = new StringBuilder();
			for (int i = 0; i < methodName.length(); i++) {
				char ch = methodName.charAt(i);
				if (ch != '<' && ch != '>') {
					newName.append(ch);
				}
			}
			methodName = newName.toString();
		}
		return ((Alias) primary).classRef.cMacroName() + "_" + methodName;
	}

	public void writeMacros(ConstantPool pool, PrintWriter out) {
		super.writeMacros(pool, out);
		String macroName = cMacroName();
		out.println("#define J9VM" + macroName + "_REF(vm) J9VMCONSTANTPOOL_VIRTUALMETHODREF_AT(vm, J9VMCONSTANTPOOL_" + macroName + ")");
		out.println("#define J9VM" + macroName + "_INDEX_AND_ARGS(vm) J9VMCONSTANTPOOL_VIRTUALMETHOD_AT(vm, J9VMCONSTANTPOOL_" + macroName + ")");
	}

}
