package org.luaj.vm2.luajc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.luaj.vm2.LoadState;
import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.DumpState;

public final class LuaJCDelegateSupport {
	private static final int HEX_CHUNK_SIZE = 60000;

	private LuaJCDelegateSupport() {
	}

	public static String[] dumpPrototypeHex(Prototype prototype) throws IOException {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		DumpState.dump(prototype, out, false);
		byte[] bytes = out.toByteArray();
		char[] hex = new char[bytes.length * 2];
		for (int i = 0, j = 0; i < bytes.length; i++) {
			int b = bytes[i] & 0xff;
			hex[j++] = Character.forDigit((b >>> 4) & 0xf, 16);
			hex[j++] = Character.forDigit(b & 0xf, 16);
		}
		String all = new String(hex);
		int count = (all.length() + HEX_CHUNK_SIZE - 1) / HEX_CHUNK_SIZE;
		String[] chunks = new String[count];
		for (int i = 0; i < count; i++) {
			int start = i * HEX_CHUNK_SIZE;
			int end = Math.min(start + HEX_CHUNK_SIZE, all.length());
			chunks[i] = all.substring(start, end);
		}
		return chunks;
	}

	public static Prototype loadPrototype(String[] hexChunks, String chunkName) {
		try {
			int totalChars = 0;
			for (int i = 0; i < hexChunks.length; i++) {
				totalChars += hexChunks[i].length();
			}
			byte[] bytes = new byte[totalChars / 2];
			int byteIndex = 0;
			for (int i = 0; i < hexChunks.length; i++) {
				String chunk = hexChunks[i];
				for (int j = 0; j < chunk.length(); j += 2) {
					int high = Character.digit(chunk.charAt(j), 16);
					int low = Character.digit(chunk.charAt(j + 1), 16);
					if (high < 0 || low < 0) {
						throw new IllegalStateException("invalid hex data in delegated luajc chunk");
					}
					bytes[byteIndex++] = (byte) ((high << 4) | low);
				}
			}
			Prototype prototype = LoadState.undump(new ByteArrayInputStream(bytes), chunkName);
			if (prototype == null) {
				throw new IllegalStateException("delegated luajc chunk did not decode as bytecode");
			}
			return prototype;
		} catch (IOException e) {
			throw new IllegalStateException("failed to load delegated luajc prototype", e);
		}
	}
}
