package jadx.core.xmlgen;

import java.io.IOException;
import java.util.Arrays;

public class CommonBinaryParser extends ParserConstants {
	protected ParserStream is;

	protected String[] parseStringPool() throws IOException {
		is.checkInt16(RES_STRING_POOL_TYPE, "String pool expected");
		return parseStringPoolNoType();
	}

	protected String[] parseStringPoolNoType() throws IOException {
		long start = is.getPos() - 2;
		is.checkInt16(0x001c, "String pool header size not 0x001c");
		long size = is.readUInt32();
		long chunkEnd = start + size;

		int stringCount = is.readInt32();
		int styleCount = is.readInt32();
		int flags = is.readInt32();
		long stringsStart = is.readInt32();
		long stylesStart = is.readInt32();

		int[] stringsOffset = is.readInt32Array(stringCount);
		int[] stylesOffset = is.readInt32Array(styleCount);

		is.checkPos(start + stringsStart, "Expected strings start");
		long stringsEnd = stylesStart == 0 ? chunkEnd : start + stylesStart;
		String[] strings = new String[stringCount];
		byte[] strArray = is.readInt8Array((int) (stringsEnd - is.getPos()));
		if ((flags & UTF8_FLAG) != 0) {
			// UTF-8
			for (int i = 0; i < stringCount; i++) {
				strings[i] = extractString8(strArray, stringsOffset[i]);
			}
		} else {
			// UTF-16
			for (int i = 0; i < stringCount; i++) {
				// don't trust specified string length, read until \0
				// stringsOffset can be same for different indexes
				strings[i] = extractString16(strArray, stringsOffset[i]);
			}
		}
		if (stylesStart != 0) {
			is.checkPos(start + stylesStart, "Expected styles start");
			if (styleCount != 0) {
				// TODO: implement styles parsing
			}
		}
		// skip padding zeroes
		is.skipToPos(chunkEnd, "Skip string pool padding");
		return strings;
	}

	private static String extractString8(byte[] strArray, int offset) {
		int start = offset + skipStrLen8(strArray, offset);
		int len = strArray[start++];
		if (len == 0) {
			return "";
		}
		if ((len & 0x80) != 0) {
			len = (len & 0x7F) << 8 | strArray[start++] & 0xFF;
		}
		byte[] arr = Arrays.copyOfRange(strArray, start, start + len);
		return new String(arr, ParserStream.STRING_CHARSET_UTF8);
	}

	private static String extractString16(byte[] strArray, int offset) {
		int len = strArray.length;
		int start = offset + skipStrLen16(strArray, offset);
		int end = start;
		while (true) {
			if (end + 1 >= len) {
				break;
			}
			if (strArray[end] == 0 && strArray[end + 1] == 0) {
				break;
			}
			end += 2;
		}
		byte[] arr = Arrays.copyOfRange(strArray, start, end);
		return new String(arr, ParserStream.STRING_CHARSET_UTF16);
	}

	private static int skipStrLen8(byte[] strArray, int offset) {
		return (strArray[offset] & 0x80) == 0 ? 1 : 2;
	}

	private static int skipStrLen16(byte[] strArray, int offset) {
		return (strArray[offset + 1] & 0x80) == 0 ? 2 : 4;
	}

	protected void die(String message) throws IOException {
		throw new IOException("Decode error: " + message
				+ ", position: 0x" + Long.toHexString(is.getPos()));
	}

}
