package speiger.src.coreengine.utils.io.dataTag.compression;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.GZIPInputStream;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.utils.io.dataTag.DataTag;
import speiger.src.coreengine.utils.io.streams.ExtendedDataInput;

public class TagDecompressor implements AutoCloseable {
	public static final int VERSION = 1;
	public static boolean LOG = false;
	protected ExtendedDataInput input;
	protected DataTagLimiter limit;
	
	public TagDecompressor(ExtendedDataInput input, DataTagLimiter limit) {
		this.input = input;
		this.limit = limit;
	}
	
	public static TagDecompressor create(ExtendedDataInput input, DataTagLimiter limit, boolean lexed) {
		return lexed ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit);
	}
	
	public static TagDecompressor createDefault(InputStream input, DataTagLimiter limit, boolean lexed) throws IOException {
		return create(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit, lexed);
	}
	
	public static TagDecompressor createFromSign(ExtendedDataInput input, DataTagLimiter limit) throws IOException {
		int sign = input.readInt();
		int version = sign >> 1;
		if(version > VERSION) throw new IllegalStateException("Version ["+version+"] is to new");
		else if(version < VERSION && LOG) System.out.println("Loading Outdated Version ["+version+"] this might cause problems");
		return (sign & 1) != 0 ? new LexedTagDeCompressor(input, limit) : new TagDecompressor(input, limit);
	}
	
	public static TagDecompressor createDefaultSign(InputStream input, DataTagLimiter limit) throws IOException {
		return createFromSign(ExtendedDataInput.createBuffered(new GZIPInputStream(input)), limit);
	}
	
	@Override
	public void close() throws Exception {
		input.close();
	}
	
	public DataTag read() throws IOException {
		return readSubTag(input.readByte(), "Init", 0);
	}
	
	public String readKey() throws IOException {
		return input.readString();
	}
	
	public DataTag readSubTag(byte type, String key, int depth) throws IOException {
		try {
			DataTag tag = DataTag.create(type);
			tag.read(input, this, depth, limit);
			return tag;
		}
		catch(IOException e) {
			throw new IllegalStateException("Crash during Reading a Tag. [Key="+key+", Type="+type+"]", e);
		}
	}
	
	public static class LexedTagDeCompressor extends TagDecompressor {
		List<String> keys = new ObjectArrayList<>();
		
		public LexedTagDeCompressor(ExtendedDataInput input, DataTagLimiter limit) {
			super(input, limit);
		}
		
		@Override
		public DataTag read() throws IOException {
			keys.clear();
			int size = input.readVarInt();
			for(int i = 0;i < size;i++) {
				keys.add(input.readString());
			}
			return super.read();
		}
		
		@Override
		public String readKey() throws IOException {
			return keys.get(input.readVarInt());
		}
	}
}
