/* csogg
 * Copyright (C) 2000 ymnk, JCraft,Inc.
 *  
 * Written by: 2000 ymnk<ymnk@jcraft.com>
 * Ported to C# from JOrbis by: Mark Crichton <crichton@gimp.org> 
 *   
 * Thanks go to the JOrbis team, for licencing the code under the
 * LGPL, making my job a lot easier.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
   
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

using System;

namespace csogg
{
	/// <summary>
	/// Summary description for csBuffer.
	/// </summary>
	public class csBuffer
	{
		private static int BUFFER_INCREMENT = 256;

		private static uint[] mask={
									  0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f,
									  0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff,
									  0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff,
									  0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff,
									  0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff,
									  0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff,
									  0x3fffffff,0x7fffffff,0xffffffff
								  };
		int ptr = 0;
		byte[] buffer = null;
		int endbit = 0;
		int endbyte = 0;
		int storage = 0;

		public void writeinit() 
		{
			buffer = new byte[BUFFER_INCREMENT];
			ptr = 0;
			buffer[0] = (byte)'\0';
			storage = BUFFER_INCREMENT;
		}

		public void write(byte[] s) 
		{
			for(int i = 0; i < s.Length; i++) 
			{
				if(s[i] == 0) break;
				write(s[i], 8);
			}
		}

		public void read (byte[] s, int bytes)
		{
			int i = 0;
			while(bytes--!=0) 
			{
				s[i++]=(byte)(read(8));
			}
		}

		void reset() 
		{
			ptr = 0;
			buffer[0] = (byte)'\0';
			endbit = endbyte = 0;
		}

		public void writeclear()
		{
			buffer = null;
		}

		public void readinit(byte[] buf, int start, int bytes)
		{
			ptr = start;
			buffer = buf;
			endbit = endbyte = 0;
			storage = bytes;
		}

		public void write(int vvalue, int bits)
		{
			if(endbyte + 4 >= storage) 
			{
				byte[] foo = new byte[storage + BUFFER_INCREMENT];
				Array.Copy(buffer, 0, foo, 0, storage);
				buffer = foo;
				storage += BUFFER_INCREMENT;
			}

			vvalue = (int)((uint)vvalue & mask[bits]);
			bits += endbit;
			buffer[ptr] |= (byte)(vvalue << endbit);

			if(bits >= 8)
			{
				buffer[ptr+1] = (byte)((uint)vvalue >> (8-endbit));
				if(bits >= 16)
				{
					buffer[ptr+2] = (byte)((uint)vvalue >> (16-endbit));
					if (bits >= 24)
					{
						buffer[ptr+3] = (byte)((uint)vvalue >> (24-endbit));
						if(bits >= 32)
						{
							if(endbit > 0)
								buffer[ptr+4] = (byte)((uint)vvalue >> (32-endbit));
							else
								buffer[ptr+4]=0;
						}
					}
				}
			}

			endbyte += bits / 8;
			ptr += bits/8;
			endbit = bits & 7;
		}

		public int look(int bits)
		{
			int ret;
			uint m = mask[bits];

			bits += endbit;

			if(endbyte + 4 >= storage)
			{
				if(endbyte+(bits-1)/8 >= storage)
					return (-1);
			}

			ret = ((buffer[ptr]) & 0xff) >> endbit;

			if(bits > 8)
			{
				ret |= ((buffer[ptr+1]) & 0xff) << (8 - endbit);
				if(bits > 16)
				{
					ret |= ((buffer[ptr+2])&0xff) << (16-endbit);
					if(bits > 24)
					{
						ret |= ((buffer[ptr+3])&0xff) << (24-endbit);
						if((bits > 32) && (endbit != 0))
						{
							ret |= ((buffer[ptr+4])&0xff) << (32-endbit);
						}
					}
				}
			}
			ret = (int)(m & ret);
			return (ret);
		}

		public int look1()
		{
			if(endbyte >= storage)
				return(-1);
			return((buffer[ptr] >> endbit) & 1);
		}

		public void adv(int bits)
		{
			bits += endbit;
			ptr += bits / 8;
			endbyte += bits / 8;
			endbit = bits & 7;
		}

		public void adv1()
		{
			++endbit;
			if(endbit > 7)
			{
				endbit = 0;
				ptr++;
				endbyte++;
			}
		}

		public int read(int bits)
		{
			int ret;
			uint m=mask[bits];

			bits += endbit;

			if(endbyte+4 >= storage)
			{
				ret = -1;
				if(endbyte + (bits-1)/8 >= storage)
				{
					ptr += bits/8;
					endbyte += bits/8;
					endbit = bits&7;
					return(ret);
				}
			}

			ret = ((buffer[ptr]) & 0xff) >> endbit;
			if(bits > 8)
			{
				ret|=((buffer[ptr+1])&0xff)<<(8-endbit);
				if(bits > 16)
				{
					ret|=((buffer[ptr+2])&0xff)<<(16-endbit);
					if(bits > 24)
					{
						ret|=((buffer[ptr+3])&0xff)<<(24-endbit);

						if((bits > 32) && (endbit != 0))
						{
							ret|=((buffer[ptr+4])&0xff)<<(32-endbit);
						}
					}
				}
			}

			ret &= (int)m;

			ptr += bits/8;
			endbyte += bits/8;
			endbit = bits&7;
			return(ret);
		}

		public int read1()
		{
			int ret;
			if(endbyte>=storage)
			{
				ret = -1;
				endbit++;
				if(endbit > 7)
				{
					endbit = 0;
					ptr++;
					endbyte++;
				}
				return(ret);
			}

			ret=(buffer[ptr] >> endbit) & 1;

			endbit++;
			if(endbit > 7)
			{
				endbit = 0;
				ptr++;
				endbyte++;
			}
			return(ret);
		}

		public int bytes()
		{
			return(endbyte+(endbit+7)/8);
		}

		public int bits()
		{
			return(endbyte*8+endbit);
		}

		public static int ilog(int v)
		{
			int ret=0;
			while(v > 0)
			{
				ret++;
				v >>= 1;
			}
			return(ret);
		}

		public byte[] buf()
		{
			return(buffer);
		}

		public csBuffer()
		{
			// Really a noop?
		}
	}
}