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

import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.function.Consumer;
import java.util.zip.GZIPOutputStream;

import speiger.src.collections.objects.maps.impl.hash.Object2IntLinkedOpenHashMap;
import speiger.src.collections.objects.maps.interfaces.Object2IntMap;
import speiger.src.coreengine.utils.io.dataTag.DataTag;
import speiger.src.coreengine.utils.io.streams.ExtendedDataOutput;

public class TagCompressor implements Flushable, AutoCloseable {
	public static final int VERSION = 1;
	protected ExtendedDataOutput output;
	
	public TagCompressor(ExtendedDataOutput output) {
		this.output = output;
	}
	
	public static TagCompressor create(ExtendedDataOutput output, boolean lexed) {
		return lexed ? new LexedTagCompressor(output) : new TagCompressor(output);
	}
	
	public static TagCompressor create(OutputStream output, boolean lexed) throws IOException {
		return create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed);
	}
	
	public static TagCompressor createAndSign(ExtendedDataOutput output, boolean lexed) throws IOException {
		return create(output, lexed).writeIdentifier();
	}
	
	public static TagCompressor createDefaultSign(OutputStream output, boolean lexed) throws IOException {
		return create(ExtendedDataOutput.createBuffered(new GZIPOutputStream(output)), lexed).writeIdentifier();
	}
	
	public TagCompressor writeIdentifier() throws IOException {
		output.writeInt(VERSION << 1 | 0);
		return this;
	}
	
	public void write(DataTag tag) throws IOException {
		output.writeByte(tag.getId());
		tag.write(output, this);
	}
	
	public void writeKey(String name) throws IOException {
		output.writeString(name);
	}
	
	@Override
	public void flush() throws IOException {
		output.flush();
	}
	
	@Override
	public void close() throws Exception {
		if(output != null) {
			output.flush();
			output.close();
			output = null;
		}
	}
	
	public static class LexedTagCompressor extends TagCompressor implements Consumer<String> {
		Object2IntMap<String> indexes = new Object2IntLinkedOpenHashMap<String>();
		
		public LexedTagCompressor(ExtendedDataOutput output) {
			super(output);
		}
		
		@Override
		public LexedTagCompressor writeIdentifier() throws IOException {
			output.writeInt(VERSION << 1 | 1);
			return this;
		}
		
		@Override
		public void write(DataTag tag) throws IOException {
			indexes.clear();
			tag.addKeys(this);
			output.writeVarInt(indexes.size());
			for(String s : indexes.keySet()) {
				output.writeString(s);
			}
			super.write(tag);
		}
		
		@Override
		public void writeKey(String name) throws IOException {
			output.writeVarInt(indexes.getInt(name));
		}
		
		@Override
		public void accept(String t) {
			indexes.putIfAbsent(t, indexes.size());
		}
	}
}
