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

import java.io.BufferedInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

public interface ExtendedDataInput extends DataInput, AutoCloseable {
	public static ExtendedDataInput createBuffered(InputStream stream) {
		return new ExtendedDataInputStream(new BufferedInputStream(stream));
	}
	
	public static ExtendedDataInput createBuffered(InputStream stream, int size) {
		return new ExtendedDataInputStream(new BufferedInputStream(stream, size));
	}
	
	public static ExtendedDataInput create(InputStream stream) {
		return new ExtendedDataInputStream(stream);
	}
	
	public default int readMedium() throws IOException {
		int ch2 = readUnsignedByte();
		int ch3 = readUnsignedByte();
		int ch4 = readUnsignedByte();
		return ((ch2 << 16) + (ch3 << 8) + (ch4 << 0));
	}
	
	public default int readVarInt() throws IOException {
		int data = 0;
		int index = 0;
		byte temp;
		do {
			temp = readByte();
			data |= (temp & 127) << index++ * 7;
			if(index > 5) throw new RuntimeException("VarInt too big");
		}
		while((temp & 128) == 128);
		return data;
	}
	
	public default long readVarLong() throws IOException {
		long data = 0L;
		int index = 0;
		byte temp;
		do {
			temp = readByte();
			data |= (long)(temp & 127) << index++ * 7;
			if(index > 10) throw new RuntimeException("VarLong too big");
		}
		while((temp & 128) == 128);
		return data;
	}
	
	public default byte[] readBytes() throws IOException {
		byte[] data = new byte[readVarInt()];
		readFully(data);
		return data;
	}
	
	public default UUID readUUID() throws IOException {
		return new UUID(readLong(), readLong());
	}
	
	public default <T extends Enum<T>> T readEnumValue(Class<T> enumType) throws IOException {
		return enumType.getEnumConstants()[readVarInt()];
	}
	
	public default String readString() throws IOException {
		return new String(readBytes(), StandardCharsets.UTF_8);
	}
	
	public static class ExtendedDataInputStream extends DataInputStream implements ExtendedDataInput {
		public ExtendedDataInputStream(InputStream in) {
			super(in);
		}
	}
	
	public static class WrappedExtendedDataInput implements ExtendedDataInput {
		DataInput input;
		
		public WrappedExtendedDataInput(DataInput input) {
			this.input = input;
		}
		
		@Override
		public void close() throws Exception {
			if(input instanceof AutoCloseable) { ((AutoCloseable)input).close(); }
		}
		
		@Override
		public void readFully(byte[] b) throws IOException {
			input.readFully(b);
		}
		
		@Override
		public void readFully(byte[] b, int off, int len) throws IOException {
			input.readFully(b, off, len);
		}
		
		@Override
		public int skipBytes(int n) throws IOException {
			return input.skipBytes(n);
		}
		
		@Override
		public boolean readBoolean() throws IOException {
			return input.readBoolean();
		}
		
		@Override
		public byte readByte() throws IOException {
			return input.readByte();
		}
		
		@Override
		public int readUnsignedByte() throws IOException {
			return input.readUnsignedByte();
		}
		
		@Override
		public short readShort() throws IOException {
			return input.readShort();
		}
		
		@Override
		public int readUnsignedShort() throws IOException {
			return input.readUnsignedShort();
		}
		
		@Override
		public char readChar() throws IOException {
			return input.readChar();
		}
		
		@Override
		public int readInt() throws IOException {
			return input.readInt();
		}
		
		@Override
		public long readLong() throws IOException {
			return input.readLong();
		}
		
		@Override
		public float readFloat() throws IOException {
			return input.readFloat();
		}
		
		@Override
		public double readDouble() throws IOException {
			return input.readDouble();
		}
		
		@Override
		public String readLine() throws IOException {
			return input.readLine();
		}
		
		@Override
		public String readUTF() throws IOException {
			return input.readUTF();
		}
	}
}