/*
	Zlib – библиотека сжатия данных общего назначения. Версия 1.1.0
	Это изменённая объектно-ориентированная версия библиотеки, полностью
	совместимая с оригинальной библиотекой.
	
	Copyright © 1995–2005 Jean-loup Gailly и Mark Adler
	Copyright © 2000–2011 ymnk, JCraft, Inc.
	Copyright © 2016, 2019 Малик Разработчик
	
	Эта библиотека поставляется «как есть», без каких-либо явных или
	подразумеваемых гарантий. Ни при каких обстоятельствах авторы не
	несут какой-либо ответственности в случае потери данных вследствие
	использования данной библиотеки.
	
	Разрешается всем использовать эту библиотеку для любых целей, в том
	числе и для коммерческих приложений, а также изменять её и
	распространять свободно при соблюдении следующих условий:
	
		1. Оригинал библиотеки не должен быть искажён; вы не должны
	заявлять, что именно вы написали оригинальную библиотеку. Если вы
	используете эту библиотеку в своём программном продукте, то ссылка
	на авторов библиотеки была бы желательна, но это не является
	обязательным требованием.
	
		2. Изменённые версии исходных текстов должны быть отчётливо
	маркированы и не должны выдаваться за оригинал библиотеки.
	
		3. Эти замечания не могут быть удалены либо изменены при
	каком-либо варианте распространения исходных текстов.
*/


package malik.emulator.compression.zlib;

public class ZStream extends Zlib
{
	int dataType;
	int nextInIndex;
	int availIn;
	int nextOutIndex;
	int availOut;
	long totalIn;
	long totalOut;
	byte[] nextIn;
	byte[] nextOut;
	Deflate dstate;
	Inflate istate;
	Checksum32 adler;
	String msg;

	public ZStream()
	{
		this.adler = new Adler32();
	}

	public void setAvailIn(int availIn)
	{
		this.availIn = availIn;
	}

	public void setAvailOut(int availOut)
	{
		this.availOut = availOut;
	}

	public void setNextIn(byte[] nextIn)
	{
		this.nextIn = nextIn;
	}

	public void setNextInIndex(int nextInIndex)
	{
		this.nextInIndex = nextInIndex;
	}

	public void setNextOut(byte[] nextOut)
	{
		this.nextOut = nextOut;
	}

	public void setNextOutIndex(int nextOutIndex)
	{
		this.nextOutIndex = nextOutIndex;
	}

	public void setOutput(byte[] dst)
	{
		setOutput(dst, 0, dst == null ? 0 : dst.length);
	}

	public void setOutput(byte[] dst, int offset, int length)
	{
		nextOut = dst;
		nextOutIndex = offset;
		availOut = length;
	}

	public void setInput(byte[] src)
	{
		setInput(src, 0, src == null ? 0 : src.length, false);
	}

	public void setInput(byte[] src, boolean append)
	{
		setInput(src, 0, src == null ? 0 : src.length, append);
	}

	public void setInput(byte[] src, int offset, int count, boolean append)
	{
		byte[] tmp;
		if(count <= 0 && append && nextIn != null)
		{
			return;
		}
		if(availIn > 0 && append)
		{
			tmp = new byte[availIn + count];
			Array.copy(nextIn, nextInIndex, tmp, 0, availIn);
			Array.copy(src, offset, tmp, availIn, count);
			nextIn = tmp;
			nextInIndex = 0;
			availIn += count;
		} else
		{
			nextIn = src;
			nextInIndex = offset;
			availIn = count;
		}
	}

	public void free()
	{
		nextIn = null;
		nextOut = null;
		msg = null;
	}

	public boolean inflateFinished()
	{
		return istate != null && istate.mode == 12;
	}

	public int inflateInit()
	{
		return inflateInit(DEF_WBITS, false);
	}

	public int inflateInit(boolean nowrap)
	{
		return inflateInit(DEF_WBITS, nowrap);
	}

	public int inflateInit(int wbits)
	{
		return inflateInit(wbits, false);
	}

	public int inflateInit(int wbits, boolean nowrap)
	{
		return (istate = new Inflate(this)).inflateInit(nowrap ? -wbits : wbits);
	}

	public int inflate(int flush)
	{
		return istate == null ? STREAM_ERROR : istate.inflate(flush);
	}

	public int inflateEnd()
	{
		int result;
		if(istate == null)
		{
			return STREAM_ERROR;
		}
		result = istate.inflateEnd();
		istate = null;
		return result;
	}

	public int inflateSync()
	{
		return istate == null ? STREAM_ERROR : istate.inflateSync();
	}

	public int inflateSyncPoint()
	{
		return istate == null ? STREAM_ERROR : istate.inflateSyncPoint();
	}

	public int inflateSetDictionary(byte[] dictionary, int dictLength)
	{
		return istate == null ? STREAM_ERROR : istate.inflateSetDictionary(dictionary, dictLength);
	}

	public int deflateInit(int level)
	{
		return deflateInit(level, MAX_WBITS, false);
	}

	public int deflateInit(int level, boolean nowrap)
	{
		return deflateInit(level, MAX_WBITS, nowrap);
	}

	public int deflateInit(int level, int bits)
	{
		return deflateInit(level, bits, false);
	}

	public int deflateInit(int level, int bits, int memlevel)
	{
		return (dstate = new Deflate(this)).deflateInit(level, bits, memlevel);
	}

	public int deflateInit(int level, int bits, boolean nowrap)
	{
		return (dstate = new Deflate(this)).deflateInit(level, nowrap ? -bits : bits);
	}

	public int deflate(int flush)
	{
		return dstate == null ? STREAM_ERROR : dstate.deflate(flush);
	}

	public int deflateEnd()
	{
		int result;
		if(dstate == null)
		{
			return STREAM_ERROR;
		}
		result = dstate.deflateEnd();
		dstate = null;
		return result;
	}

	public int deflateParams(int level, int strategy)
	{
		return dstate == null ? STREAM_ERROR : dstate.deflateParams(level, strategy);
	}

	public int deflateSetDictionary(byte[] dictionary, int dictLength)
	{
		return dstate == null ? STREAM_ERROR : dstate.deflateSetDictionary(dictionary, dictLength);
	}

	public int getAdler()
	{
		return adler.getValue();
	}

	public int getAvailIn()
	{
		return availIn;
	}

	public int getAvailOut()
	{
		return availOut;
	}

	public int getNextInIndex()
	{
		return nextInIndex;
	}

	public int getNextOutIndex()
	{
		return nextOutIndex;
	}

	public long getTotalIn()
	{
		return totalIn;
	}

	public long getTotalOut()
	{
		return totalOut;
	}

	public byte[] getNextIn()
	{
		return nextIn;
	}

	public byte[] getNextOut()
	{
		return nextOut;
	}

	public String getMessage()
	{
		return msg;
	}

	final void flushPending()
	{
		int len = dstate.pending;
		if(len > availOut)
		{
			len = availOut;
		}
		if(len == 0)
		{
			return;
		}
		Array.copy(dstate.pendingBuf, dstate.pendingOut, nextOut, nextOutIndex, len);
		nextOutIndex += len;
		dstate.pendingOut += len;
		totalOut += len;
		availOut -= len;
		dstate.pending -= len;
		if(dstate.pending == 0)
		{
			dstate.pendingOut = 0;
		}
	}

	final int readBuf(byte[] dst, int offset, int count)
	{
		int length = availIn;
		if(length > count)
		{
			length = count;
		}
		if(length == 0)
		{
			return 0;
		}
		availIn -= length;
		if(dstate.wrap != 0)
		{
			adler.update(nextIn, nextInIndex, length);
		}
		Array.copy(nextIn, nextInIndex, dst, offset, length);
		nextInIndex += length;
		totalIn += length;
		return length;
	}
}
