// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.util;

import java.io.UnsupportedEncodingException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

public class ByteBufferUtils {
	public static String getString(ByteBuffer bb)
			throws BufferUnderflowException {
		return getString(bb, false, false);
	}

	public static String getString(ByteBuffer bb, boolean switchEndian, boolean small)
			throws BufferUnderflowException {
		String out = "";
		synchronized (bb) {

			//This version works with non-latin characters
			int stringLen;
			if (small)
				stringLen = (int)bb.get();
			else
				stringLen = bb.getInt();
			if (switchEndian)
				stringLen = ((Integer.reverseBytes(stringLen)) * 2);
			else
				stringLen *= 2;
			byte[] b = new byte[stringLen];
			for (int i=0;i<stringLen;i+=2) {
				if (switchEndian) {
					b[i+1] = bb.get();
					b[i] = bb.get();
				} else {
					b[i] = bb.get();
					b[i+1] = bb.get();
				}
			}
			try {
				out = new String(b, "UTF-16BE");
			} catch (UnsupportedEncodingException e) {}
		}
		return out;
	}

	public static void putString(ByteBuffer bb, String data, boolean small)
			throws BufferOverflowException {
		putString(bb, data, false, small);
	}

	public static void putString(ByteBuffer bb, String data,
			boolean switchEndian, boolean small) throws BufferOverflowException {
		if (data == null) {
			data = "";
		}
		char[] chars = data.toCharArray();

		int length = chars.length;
		if (small && length > 255)
			length = 255; //limit for smallString

		synchronized (bb) {
			// Write length
			if (small)
				bb.put((byte)length);
			else {
				if (switchEndian) {
					bb.putInt(Integer.reverseBytes(length));
				} else {
					bb.putInt(length);
				}
			}
			// Write chars
			for (int i=0;i<length;i++) {
				char c = chars[i];
				if (switchEndian) {
					bb.putChar(Character.reverseBytes(c));
				} else {
					bb.putChar(c);
				}
			}
		}
	}

	public static String getHexString(ByteBuffer bb)
			throws BufferUnderflowException {
		return getHexString(bb, false);
	}

	public static String getHexString(ByteBuffer bb, boolean switchEndian)
			throws BufferUnderflowException {
		String out = "";
		synchronized (bb) {
			// Read len
			int stringLen = bb.getInt();

			if (switchEndian) {
				stringLen = Integer.reverseBytes(stringLen);
			}

			// Read len worth of chars
			for (int i = 0; i < stringLen; ++i) {
				out += Integer.toString((bb.get() & 0xff) + 0x100, 16)
						.substring(1);
			}
		}
		return out;
	}

	public static void putHexString(ByteBuffer bb, String data)
			throws BufferOverflowException {
		putHexString(bb, data, false);
	}

	public static void putHexString(ByteBuffer bb, String data,
			boolean switchEndian) throws BufferOverflowException {

		if (data == null) {
			data = "";
		}

		byte[] bts = new byte[data.length() / 2];

		for (int i = 0; i < bts.length; i++) {
			bts[i] = (byte) Integer.parseInt(data.substring(2 * i, 2 * i + 1),
					16);
		}

		synchronized (bb) {
			if (switchEndian) {
				bb.putInt(Integer.reverseBytes(data.length() / 2));
			} else {
				bb.putInt(data.length() / 2);
			}
			bb.put(bts);
		}
	}

	public static String getUnicodeString(ByteBuffer bb)
			throws BufferUnderflowException {
		return getUnicodeString(bb, false);
	}

	public static String getUnicodeString(ByteBuffer bb, boolean switchEndian)
			throws BufferUnderflowException {
		byte[] out;
		short thisChar;
		synchronized (bb) {
			// Read len
			int stringLen = bb.getInt();
			if (switchEndian) {
				stringLen = Integer.reverseBytes(stringLen);
			}
			out = new byte[stringLen];
			// Read len worth of chars
			for (int i = 0; i < stringLen; ++i) {
				thisChar = bb.getShort();
				if (switchEndian)
					Short.reverseBytes(thisChar);
				out[i] = (byte) (thisChar & 0xff); // ignore first byte
			}
		}
		return new String(out);
	}

	public static void putUnicodeString(ByteBuffer bb, String data)
			throws BufferOverflowException {
		putUnicodeString(bb, data, false);
	}

	public static void putUnicodeString(ByteBuffer bb, String data,
			boolean switchEndian) throws BufferOverflowException {
		byte[] out;
		short thisChar;
		if (data == null)
			return;
		out = new byte[data.length()];
		out = data.getBytes();

		for (byte b : out) {
			thisChar = b;
			if (switchEndian)
				thisChar = Short.reverseBytes(thisChar);
			bb.putShort(thisChar);
		}
	}

	public static boolean checkByteBufferNearFull(ByteBuffer bb) {
        return bb.position() >= (bb.capacity() * 0.9);
    }

	//FIXME: Replace these!!!
//	public static ByteBuffer resizeByteBuffer(ByteBuffer bb, int multiplyer) {
//
//		ByteBuffer out = ByteBuffer.allocate(bb.capacity() * multiplyer);
//
//		// Copy the data to a temp buf
//		bb.flip();
//		out.put(bb);
//
//		return out;
//	}
//
//	public static ByteBuffer shrinkByteBuffer(ByteBuffer bb) {
//
//		bb.flip();
//		ByteBuffer out = ByteBuffer.allocate(bb.remaining());
//		out.put(bb);
//		return out;
//	}

}