package ja.bytecode;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.Map;

public class BootstrapMethodsAttribute extends AttributeInfo {
	/**
	 * The name of this attribute <code>"BootstrapMethods"</code>.
	 */
	public static final String tag = "BootstrapMethods";

	/**
	 * An element of <code>bootstrap_methods</code>.
	 */
	public static class BootstrapMethod {
		/**
		 * Constructs an element of <code>bootstrap_methods</code>.
		 * 
		 * @param method
		 *            <code>bootstrap_method_ref</code>.
		 * @param args
		 *            <code>bootstrap_arguments</code>.
		 */
		public BootstrapMethod(int method, int[] args) {
			methodRef = method;
			arguments = args;
		}

		/**
		 * <code>bootstrap_method_ref</code>. The value at this index must be a
		 * <code>CONSTANT_MethodHandle_info</code>.
		 */
		public int methodRef;

		/**
		 * <code>bootstrap_arguments</code>.
		 */
		public int[] arguments;
	}

	BootstrapMethodsAttribute(ConstPool cp, int n, DataInputStream in)
			throws IOException {
		super(cp, n, in);
	}

	/**
	 * Constructs a BootstrapMethods attribute.
	 * 
	 * @param cp
	 *            a constant pool table.
	 * @param methods
	 *            the contents.
	 */
	public BootstrapMethodsAttribute(ConstPool cp, BootstrapMethod[] methods) {
		super(cp, tag);
		int size = 2;
		for (int i = 0; i < methods.length; i++)
			size += 4 + methods[i].arguments.length * 2;

		byte[] data = new byte[size];
		ByteArray.write16bit(methods.length, data, 0); // num_bootstrap_methods
		int pos = 2;
		for (int i = 0; i < methods.length; i++) {
			ByteArray.write16bit(methods[i].methodRef, data, pos);
			ByteArray.write16bit(methods[i].arguments.length, data, pos + 2);
			int[] args = methods[i].arguments;
			pos += 4;
			for (int k = 0; k < args.length; k++) {
				ByteArray.write16bit(args[k], data, pos);
				pos += 2;
			}
		}

		set(data);
	}

	/**
	 * Obtains <code>bootstrap_methods</code> in this attribute.
	 * 
	 * @return an array of <code>BootstrapMethod</code>. Since it is a fresh
	 *         copy, modifying the returned array does not affect the original
	 *         contents of this attribute.
	 */
	public BootstrapMethod[] getMethods() {
		byte[] data = this.get();
		int num = ByteArray.readU16bit(data, 0);
		BootstrapMethod[] methods = new BootstrapMethod[num];
		int pos = 2;
		for (int i = 0; i < num; i++) {
			int ref = ByteArray.readU16bit(data, pos);
			int len = ByteArray.readU16bit(data, pos + 2);
			int[] args = new int[len];
			pos += 4;
			for (int k = 0; k < len; k++) {
				args[k] = ByteArray.readU16bit(data, pos);
				pos += 2;
			}

			methods[i] = new BootstrapMethod(ref, args);
		}

		return methods;
	}

	/**
	 * Makes a copy. Class names are replaced according to the given
	 * <code>Map</code> object.
	 * 
	 * @param newCp
	 *            the constant pool table used by the new copy.
	 * @param classnames
	 *            pairs of replaced and substituted class names.
	 */
	public AttributeInfo copy(ConstPool newCp, Map classnames) {
		BootstrapMethod[] methods = getMethods();
		ConstPool thisCp = getConstPool();
		for (int i = 0; i < methods.length; i++) {
			BootstrapMethod m = methods[i];
			m.methodRef = thisCp.copy(m.methodRef, newCp, classnames);
			for (int k = 0; k < m.arguments.length; k++)
				m.arguments[k] = thisCp.copy(m.arguments[k], newCp, classnames);
		}

		return new BootstrapMethodsAttribute(newCp, methods);
	}
}
