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


package engine.net;

import engine.math.Vector3f;
import engine.math.Vector3fImmutable;
import engine.util.ByteBufferUtils;
import org.joda.time.DateTime;

import java.nio.ByteBuffer;
import java.time.LocalDateTime;
import java.util.UUID;

//TODO possibly pool this class? Maybe make them static?
//TODO need to extract the SB specific stuff from here into subclass.

public class ByteBufferWriter {

    private final ByteBuffer bb;

    public ByteBufferWriter(ByteBuffer bb) {
        super();
        this.bb = bb;
    }

    /*
     * Putters
     */

    public synchronized void put(byte x) {

        this.bb.put(x);
    }

    public synchronized void putChar(char x) {

        this.bb.putChar(x);
    }

    public synchronized void putShort(short x) {

        this.bb.putShort(x);
    }

    public synchronized void putInt(int x) {
        this.bb.putInt(x);
    }

    public synchronized void putLong(long x) {
        this.bb.putLong(x);
    }

    public synchronized void putDateTime(DateTime dateTime) {
        this.put((byte) dateTime.getDayOfMonth());
        this.put((byte) ((byte) dateTime.getMonthOfYear() - 1));
        this.putInt(dateTime.getYear() - 1900);
        this.put((byte) dateTime.getHourOfDay());
        this.put((byte) dateTime.getMinuteOfHour());
        this.put((byte) dateTime.getSecondOfDay());
    }

    public synchronized void putLocalDateTime(LocalDateTime dateTime) {
        this.put((byte) dateTime.getDayOfMonth());
        this.put((byte) ((byte) dateTime.getMonth().getValue() - 1));
        this.putInt(dateTime.getYear() - 1900);
        this.put((byte) dateTime.getHour());
        this.put((byte) dateTime.getMinute());
        this.put((byte) dateTime.getSecond());
    }

    public synchronized void putFloat(float x) {
        this.bb.putFloat(x);
    }

    public synchronized void putDouble(double x) {
        this.bb.putDouble(x);
    }

    public synchronized void putString(String x) {
        ByteBufferUtils.putString(this.bb, x, false, false);
    }

    public synchronized void putSmallString(String x) {
        ByteBufferUtils.putString(this.bb, x, false, true);
    }

    public synchronized void putHexString(String x) {
        ByteBufferUtils.putHexString(this.bb, x, false);
    }

    public synchronized void putHexStringWithoutSize(String data) {
        int length = data.length() / 2;

        for (int i = 0; i < length; i++) {
            bb.put((byte) Integer.parseInt(data.substring(2 * i, 2 * i + 2), 16));
        }
    }

    public synchronized void putUnicodeString(String x) {
        ByteBufferUtils.putUnicodeString(this.bb, x, false);
    }

    public synchronized void putWriter(ByteBufferWriter writer) {
        synchronized (writer) {
            ByteBuffer bbin = writer.bb;
            bbin.flip();
            this.bb.put(bbin);
        }
    }

    public synchronized void putBB(ByteBuffer bb) {
        synchronized (bb) {
            bb.flip();
            this.bb.put(bb);
        }
    }

    public synchronized void putIntAt(int value, int position) {
        // mark end position
        int endPosition = this.position();

        // go back to the desired position
        this.bb.position(position);

        // Write in the value:
        this.putInt(value);

        // now go back to end:
        this.bb.position(endPosition);
    }

    public synchronized void putVector3f(Vector3f x) {
        this.bb.putFloat(x.x);
        this.bb.putFloat(x.y);
        this.bb.putFloat(x.z);
    }

    public synchronized void putVector3f(Vector3fImmutable x) {
        this.bb.putFloat(x.x);
        this.bb.putFloat(x.y);
        this.bb.putFloat(x.z);
    }

    public synchronized void putUUID(final UUID uuid) {
        final long msb = uuid.getMostSignificantBits();
        final long lsb = uuid.getLeastSignificantBits();
        final byte[] buffer = new byte[16];

        for (int i = 0; i < 8; i++) {
            buffer[i] = (byte) (msb >>> 8 * (7 - i));
        }

        for (int i = 8; i < 16; i++) {
            buffer[i] = (byte) (lsb >>> 8 * (7 - i));
        }

        this.bb.put(buffer);
    }

    /**
     * @return
     * @see java.nio.Buffer#hasRemaining()
     */
    public final boolean hasRemaining() {
        return bb.hasRemaining();
    }

    /**
     * @return
     * @see java.nio.Buffer#limit()
     */
    public final int limit() {
        return bb.limit();
    }

    /**
     * @return
     * @see java.nio.Buffer#position()
     */
    public final int position() {
        return bb.position();
    }

    /**
     * @return
     * @see java.nio.Buffer#remaining()
     */
    public final int remaining() {
        return bb.remaining();
    }

    /*
     * Getters
     */

    public synchronized ByteBuffer getBb() {
        return this.bb;
    }
}
