using System;

namespace ComponentAce.Compression.Libs.zlib
{
    internal sealed class InfBlocks
    {
    	private const int MANY = 1440;

    	private static readonly int[] inflate_mask = new int[17]
    	{
    		0, 1, 3, 7, 15, 31, 63, 127, 255, 511,
    		1023, 2047, 4095, 8191, 16383, 32767, 65535
    	};

    	internal static readonly int[] border = new int[19]
    	{
    		16, 17, 18, 0, 8, 7, 9, 6, 10, 5,
    		11, 4, 12, 3, 13, 2, 14, 1, 15
    	};

    	private const int Z_OK = 0;

    	private const int Z_STREAM_END = 1;

    	private const int Z_NEED_DICT = 2;

    	private const int Z_ERRNO = -1;

    	private const int Z_STREAM_ERROR = -2;

    	private const int Z_DATA_ERROR = -3;

    	private const int Z_MEM_ERROR = -4;

    	private const int Z_BUF_ERROR = -5;

    	private const int Z_VERSION_ERROR = -6;

    	private const int TYPE = 0;

    	private const int LENS = 1;

    	private const int STORED = 2;

    	private const int TABLE = 3;

    	private const int BTREE = 4;

    	private const int DTREE = 5;

    	private const int CODES = 6;

    	private const int DRY = 7;

    	private const int DONE = 8;

    	private const int BAD = 9;

    	internal int mode;

    	internal int left;

    	internal int table;

    	internal int index;

    	internal int[] blens;

    	internal int[] bb = new int[1];

    	internal int[] tb = new int[1];

    	internal InfCodes codes;

    	internal int last;

    	internal int bitk;

    	internal int bitb;

    	internal int[] hufts;

    	internal byte[] window;

    	internal int end;

    	internal int read;

    	internal int write;

    	internal object checkfn;

    	internal long check;

    	internal InfBlocks(ZStream z, object checkfn, int w)
    	{
    		hufts = new int[4320];
    		window = new byte[w];
    		end = w;
    		this.checkfn = checkfn;
    		mode = 0;
    		reset(z, null);
    	}

    	internal void reset(ZStream z, long[] c)
    	{
    		if (c != null)
    		{
    			c[0] = check;
    		}
    		if (mode == 4 || mode == 5)
    		{
    			blens = null;
    		}
    		if (mode == 6)
    		{
    			codes.free(z);
    		}
    		mode = 0;
    		bitk = 0;
    		bitb = 0;
    		read = (write = 0);
    		if (checkfn != null)
    		{
    			z.adler = (check = z._adler.adler32(0L, null, 0, 0));
    		}
    	}

    	internal int proc(ZStream z, int r)
    	{
    		int num = z.next_in_index;
    		int num2 = z.avail_in;
    		int num3 = bitb;
    		int i = bitk;
    		int num4 = write;
    		int num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    		while (true)
    		{
    			switch (mode)
    			{
    			case 0:
    			{
    				for (; i < 3; i += 8)
    				{
    					if (num2 != 0)
    					{
    						r = 0;
    						num2--;
    						num3 |= (z.next_in[num++] & 0xFF) << i;
    						continue;
    					}
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				int num6 = num3 & 7;
    				last = num6 & 1;
    				switch (SupportClass.URShift(num6, 1))
    				{
    				case 0:
    					num3 = SupportClass.URShift(num3, 3);
    					i -= 3;
    					num6 = i & 7;
    					num3 = SupportClass.URShift(num3, num6);
    					i -= num6;
    					mode = 1;
    					break;
    				case 1:
    				{
    					int[] array5 = new int[1];
    					int[] array6 = new int[1];
    					int[][] array7 = new int[1][];
    					int[][] array8 = new int[1][];
    					InfTree.inflate_trees_fixed(array5, array6, array7, array8, z);
    					codes = new InfCodes(array5[0], array6[0], array7[0], array8[0], z);
    					num3 = SupportClass.URShift(num3, 3);
    					i -= 3;
    					mode = 6;
    					break;
    				}
    				case 2:
    					num3 = SupportClass.URShift(num3, 3);
    					i -= 3;
    					mode = 3;
    					break;
    				case 3:
    					num3 = SupportClass.URShift(num3, 3);
    					i -= 3;
    					mode = 9;
    					z.msg = "invalid block type";
    					r = -3;
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				break;
    			}
    			case 1:
    				for (; i < 32; i += 8)
    				{
    					if (num2 != 0)
    					{
    						r = 0;
    						num2--;
    						num3 |= (z.next_in[num++] & 0xFF) << i;
    						continue;
    					}
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				if ((SupportClass.URShift(~num3, 16) & 0xFFFF) != (num3 & 0xFFFF))
    				{
    					mode = 9;
    					z.msg = "invalid stored block lengths";
    					r = -3;
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				left = num3 & 0xFFFF;
    				num3 = (i = 0);
    				mode = ((left != 0) ? 2 : ((last != 0) ? 7 : 0));
    				break;
    			case 2:
    			{
    				if (num2 == 0)
    				{
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				if (num5 == 0)
    				{
    					if (num4 == end && read != 0)
    					{
    						num4 = 0;
    						num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    					}
    					if (num5 == 0)
    					{
    						write = num4;
    						r = inflate_flush(z, r);
    						num4 = write;
    						num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    						if (num4 == end && read != 0)
    						{
    							num4 = 0;
    							num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    						}
    						if (num5 == 0)
    						{
    							bitb = num3;
    							bitk = i;
    							z.avail_in = num2;
    							z.total_in += num - z.next_in_index;
    							z.next_in_index = num;
    							write = num4;
    							return inflate_flush(z, r);
    						}
    					}
    				}
    				r = 0;
    				int num6 = left;
    				if (num6 > num2)
    				{
    					num6 = num2;
    				}
    				if (num6 > num5)
    				{
    					num6 = num5;
    				}
    				Array.Copy(z.next_in, num, window, num4, num6);
    				num += num6;
    				num2 -= num6;
    				num4 += num6;
    				num5 -= num6;
    				if ((left -= num6) == 0)
    				{
    					mode = ((last != 0) ? 7 : 0);
    				}
    				break;
    			}
    			case 3:
    			{
    				for (; i < 14; i += 8)
    				{
    					if (num2 != 0)
    					{
    						r = 0;
    						num2--;
    						num3 |= (z.next_in[num++] & 0xFF) << i;
    						continue;
    					}
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				int num6 = (table = num3 & 0x3FFF);
    				if ((num6 & 0x1F) > 29 || ((num6 >> 5) & 0x1F) > 29)
    				{
    					mode = 9;
    					z.msg = "too many length or distance symbols";
    					r = -3;
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				num6 = 258 + (num6 & 0x1F) + ((num6 >> 5) & 0x1F);
    				blens = new int[num6];
    				num3 = SupportClass.URShift(num3, 14);
    				i -= 14;
    				index = 0;
    				mode = 4;
    				goto case 4;
    			}
    			case 4:
    			{
    				while (index < 4 + SupportClass.URShift(table, 10))
    				{
    					for (; i < 3; i += 8)
    					{
    						if (num2 != 0)
    						{
    							r = 0;
    							num2--;
    							num3 |= (z.next_in[num++] & 0xFF) << i;
    							continue;
    						}
    						bitb = num3;
    						bitk = i;
    						z.avail_in = num2;
    						z.total_in += num - z.next_in_index;
    						z.next_in_index = num;
    						write = num4;
    						return inflate_flush(z, r);
    					}
    					blens[border[index++]] = num3 & 7;
    					num3 = SupportClass.URShift(num3, 3);
    					i -= 3;
    				}
    				while (index < 19)
    				{
    					blens[border[index++]] = 0;
    				}
    				bb[0] = 7;
    				int num6 = InfTree.inflate_trees_bits(blens, bb, tb, hufts, z);
    				if (num6 != 0)
    				{
    					r = num6;
    					if (r == -3)
    					{
    						blens = null;
    						mode = 9;
    					}
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				index = 0;
    				mode = 5;
    				goto case 5;
    			}
    			case 5:
    			{
    				int num6;
    				while (true)
    				{
    					num6 = table;
    					if (index >= 258 + (num6 & 0x1F) + ((num6 >> 5) & 0x1F))
    					{
    						break;
    					}
    					for (num6 = bb[0]; i < num6; i += 8)
    					{
    						if (num2 != 0)
    						{
    							r = 0;
    							num2--;
    							num3 |= (z.next_in[num++] & 0xFF) << i;
    							continue;
    						}
    						bitb = num3;
    						bitk = i;
    						z.avail_in = num2;
    						z.total_in += num - z.next_in_index;
    						z.next_in_index = num;
    						write = num4;
    						return inflate_flush(z, r);
    					}
    					_ = tb[0];
    					_ = -1;
    					num6 = hufts[(tb[0] + (num3 & inflate_mask[num6])) * 3 + 1];
    					int num7 = hufts[(tb[0] + (num3 & inflate_mask[num6])) * 3 + 2];
    					if (num7 < 16)
    					{
    						num3 = SupportClass.URShift(num3, num6);
    						i -= num6;
    						blens[index++] = num7;
    						continue;
    					}
    					int num8 = ((num7 == 18) ? 7 : (num7 - 14));
    					int num9 = ((num7 == 18) ? 11 : 3);
    					for (; i < num6 + num8; i += 8)
    					{
    						if (num2 != 0)
    						{
    							r = 0;
    							num2--;
    							num3 |= (z.next_in[num++] & 0xFF) << i;
    							continue;
    						}
    						bitb = num3;
    						bitk = i;
    						z.avail_in = num2;
    						z.total_in += num - z.next_in_index;
    						z.next_in_index = num;
    						write = num4;
    						return inflate_flush(z, r);
    					}
    					num3 = SupportClass.URShift(num3, num6);
    					i -= num6;
    					num9 += num3 & inflate_mask[num8];
    					num3 = SupportClass.URShift(num3, num8);
    					i -= num8;
    					num8 = index;
    					num6 = table;
    					if (num8 + num9 > 258 + (num6 & 0x1F) + ((num6 >> 5) & 0x1F) || (num7 == 16 && num8 < 1))
    					{
    						blens = null;
    						mode = 9;
    						z.msg = "invalid bit length repeat";
    						r = -3;
    						bitb = num3;
    						bitk = i;
    						z.avail_in = num2;
    						z.total_in += num - z.next_in_index;
    						z.next_in_index = num;
    						write = num4;
    						return inflate_flush(z, r);
    					}
    					num7 = ((num7 == 16) ? blens[num8 - 1] : 0);
    					do
    					{
    						blens[num8++] = num7;
    					}
    					while (--num9 != 0);
    					index = num8;
    				}
    				tb[0] = -1;
    				int[] array = new int[1];
    				int[] array2 = new int[1];
    				int[] array3 = new int[1];
    				int[] array4 = new int[1];
    				array[0] = 9;
    				array2[0] = 6;
    				num6 = table;
    				num6 = InfTree.inflate_trees_dynamic(257 + (num6 & 0x1F), 1 + ((num6 >> 5) & 0x1F), blens, array, array2, array3, array4, hufts, z);
    				if (num6 != 0)
    				{
    					if (num6 == -3)
    					{
    						blens = null;
    						mode = 9;
    					}
    					r = num6;
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				codes = new InfCodes(array[0], array2[0], hufts, array3[0], hufts, array4[0], z);
    				blens = null;
    				mode = 6;
    				goto case 6;
    			}
    			case 6:
    				bitb = num3;
    				bitk = i;
    				z.avail_in = num2;
    				z.total_in += num - z.next_in_index;
    				z.next_in_index = num;
    				write = num4;
    				if ((r = codes.proc(this, z, r)) != 1)
    				{
    					return inflate_flush(z, r);
    				}
    				r = 0;
    				codes.free(z);
    				num = z.next_in_index;
    				num2 = z.avail_in;
    				num3 = bitb;
    				i = bitk;
    				num4 = write;
    				num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    				if (last == 0)
    				{
    					mode = 0;
    					break;
    				}
    				mode = 7;
    				goto case 7;
    			case 7:
    				write = num4;
    				r = inflate_flush(z, r);
    				num4 = write;
    				num5 = ((num4 < read) ? (read - num4 - 1) : (end - num4));
    				if (read != write)
    				{
    					bitb = num3;
    					bitk = i;
    					z.avail_in = num2;
    					z.total_in += num - z.next_in_index;
    					z.next_in_index = num;
    					write = num4;
    					return inflate_flush(z, r);
    				}
    				mode = 8;
    				goto case 8;
    			case 8:
    				r = 1;
    				bitb = num3;
    				bitk = i;
    				z.avail_in = num2;
    				z.total_in += num - z.next_in_index;
    				z.next_in_index = num;
    				write = num4;
    				return inflate_flush(z, r);
    			case 9:
    				r = -3;
    				bitb = num3;
    				bitk = i;
    				z.avail_in = num2;
    				z.total_in += num - z.next_in_index;
    				z.next_in_index = num;
    				write = num4;
    				return inflate_flush(z, r);
    			default:
    				r = -2;
    				bitb = num3;
    				bitk = i;
    				z.avail_in = num2;
    				z.total_in += num - z.next_in_index;
    				z.next_in_index = num;
    				write = num4;
    				return inflate_flush(z, r);
    			}
    		}
    	}

    	internal void free(ZStream z)
    	{
    		reset(z, null);
    		window = null;
    		hufts = null;
    	}

    	internal void set_dictionary(byte[] d, int start, int n)
    	{
    		Array.Copy(d, start, window, 0, n);
    		read = (write = n);
    	}

    	internal int sync_point()
    	{
    		if (mode != 1)
    		{
    			return 0;
    		}
    		return 1;
    	}

    	internal int inflate_flush(ZStream z, int r)
    	{
    		int next_out_index = z.next_out_index;
    		int num = read;
    		int num2 = ((num <= write) ? write : end) - num;
    		if (num2 > z.avail_out)
    		{
    			num2 = z.avail_out;
    		}
    		if (num2 != 0 && r == -5)
    		{
    			r = 0;
    		}
    		z.avail_out -= num2;
    		z.total_out += num2;
    		if (checkfn != null)
    		{
    			z.adler = (check = z._adler.adler32(check, window, num, num2));
    		}
    		Array.Copy(window, num, z.next_out, next_out_index, num2);
    		next_out_index += num2;
    		num += num2;
    		if (num == end)
    		{
    			num = 0;
    			if (write == end)
    			{
    				write = 0;
    			}
    			num2 = write - num;
    			if (num2 > z.avail_out)
    			{
    				num2 = z.avail_out;
    			}
    			if (num2 != 0 && r == -5)
    			{
    				r = 0;
    			}
    			z.avail_out -= num2;
    			z.total_out += num2;
    			if (checkfn != null)
    			{
    				z.adler = (check = z._adler.adler32(check, window, num, num2));
    			}
    			Array.Copy(window, num, z.next_out, next_out_index, num2);
    			next_out_index += num2;
    			num += num2;
    		}
    		z.next_out_index = next_out_index;
    		read = num;
    		return r;
    	}
    }
}