package speiger.src.coreengine.utils.io.streams;

import java.io.BufferedOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

public interface ExtendedDataOutput extends DataOutput, Flushable, AutoCloseable {
	public static ExtendedDataOutput createBuffered(OutputStream stream) {
		return new ExtendedDataOutputStream(new BufferedOutputStream(stream));
	}
	
	public static ExtendedDataOutput createBuffered(OutputStream stream, int size) {
		return new ExtendedDataOutputStream(new BufferedOutputStream(stream, size));
	}
	
	public static ExtendedDataOutput create(OutputStream stream) {
		return new ExtendedDataOutputStream(stream);
	}
	
	public default void writeMedium(int v) throws IOException {
		write((v >>> 16) & 0xFF);
		write((v >>> 8) & 0xFF);
		write((v >>> 0) & 0xFF);
	}
	
	public default void writeVarInt(int v) throws IOException {
		while((v & -128) != 0) {
			writeByte(v & 127 | 128);
			v >>>= 7;
		}
		writeByte(v);
	}
	
	public default void writeVarLong(long v) throws IOException {
		while((v & -128L) != 0L) {
			writeByte((int)(v & 127L) | 128);
			v >>>= 7;
		}
		writeByte((int)v);
	}
	
	public default void writeBytes(byte[] v) throws IOException {
		writeBytes(v, 0, v.length);
	}
	
	public default void writeBytes(byte[] v, int offset, int length) throws IOException {
		writeVarInt(length);
		write(v, offset, length);
	}
	
	public default void writeUUID(UUID id) throws IOException {
		writeLong(id.getMostSignificantBits());
		writeLong(id.getLeastSignificantBits());
	}
	
	public default void writeEnum(Enum<?> v) throws IOException {
		writeVarInt(v.ordinal());
	}
	
	public default void writeString(String s) throws IOException {
		writeString(s, 65535);
	}
	
	public default void writeString(String s, int maxBytes) throws IOException {
		byte[] data = s.getBytes(StandardCharsets.UTF_8);
		if(data.length > maxBytes) { throw new IOException("Encoded String was to big. ("+data.length+", Limit: "+maxBytes+", Diff: "+(data.length - maxBytes)+")"); }
		writeBytes(data);
	}
	
	public static class ExtendedDataOutputStream extends DataOutputStream implements ExtendedDataOutput {
		public ExtendedDataOutputStream(OutputStream out) {
			super(out);
		}
	}
}
