package com.one;

import com.vmx.AuxClass;
import java.io.*;


/**
 * Входной поток с полной буферизацией.
 * Призван работать там, где требуется поддержка mark / reset, но ее почему - то нет.
 */
public class BufferedInputStream extends RandomAccessInputStream
{
	protected InputStream is = null;
	protected byte[] buf = null;
	protected int currpos;   // позиция первого несчитанного байта
	protected int readpos;   // позиция первого неинициализированного байта 
	protected int markedpos; // маркированная позиция

	public BufferedInputStream(InputStream is) throws IOException
	{
		this(is, true, true);
	}
	
	public BufferedInputStream(InputStream is, boolean prebuffer, boolean closeafter) throws IOException
	{
		if(prebuffer)
		{
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] b = new byte[AuxClass.ARCBUFSIZE];
			int len;

			try
			{
				while(true)
				{
					len = is.read(b);

					if(len > 0)
					{
						baos.write(b, 0, len);
					}
					else
					{
						break;
					}
				}

				if(closeafter)
				{
					is.close();
				}

				is = null;
				b = null;

				System.gc();

				buf = baos.toByteArray();
				readpos = buf.length;

				baos.close();
			}
			catch(OutOfMemoryError oome)
			{
				baos.close();
				throw new IOException("Consider this as \"OutOfMemoryError\"");
			}
		}
		else
		{
			this.is = is;
			
			System.gc();

			buf = new byte[is.available()];
			readpos = 0;
		}

		currpos = 0;
		markedpos = 0;
	}
	
	public BufferedInputStream(byte[] data)
	{
		this.is = null;
		
		buf = data;
		
		currpos = 0;
		readpos = data.length;
		markedpos = 0;
	}

	public byte[] getBuffer()
	{
		return buf;
	}
	
	public int available()
	{
		return buf.length - currpos;
	}
	
	public void close() throws IOException
	{
		if(is != null)
		{
			is.close();
			is = null;
		}
		
		buf = null;
		
		System.gc();
	}
	
	public void mark(int readLimit)
	{
		markedpos = currpos;
	}
	
	public int read() throws IOException
	{
		if(currpos >= buf.length) // конец потока
		{
			return -1;
		}
		
		bufferize(currpos);
		
		return ((int)buf[currpos++]) & 0xFF;
	}
	
	public int read(byte[] b) throws IOException
	{
		return read(b, 0, b.length);
	}
	
	public int read(byte[] b, int off, int len) throws IOException
	{
		int available = available();

		if(available <= 0)
		{
			return -1;
		}

		if(len > available)
		{
			len = available;
		}
		
		bufferize(currpos + len - 1);
		
		System.arraycopy(buf, currpos, b, off, len);
		
		currpos += len;
		
		return len;
	}
	
	public void reset()
	{
		currpos = markedpos;
	}
	
	public long skip(long n)
	{
		if(n > available())
		{
			n = available();
		}
		
		currpos += n;
		
		return n;
	}
	
	public void setPosition(int pos)
	{
		if(pos == currpos)
		{
			return;
		}
		
		if(pos < 0)
		{
			currpos = 0;
		}
		else if(pos > getCapacity())
		{
			currpos = getCapacity();
		}
		else
		{
			currpos = pos;
		}
	}
	
	public int getPosition()
	{
		return currpos;
	}
	
	public int getCapacity()
	{
		return buf.length;
	}
	
	public void update()
	{
	}

	public void pause()
	{
	}

	public void resume()
	{
	}
	
	/**
	 * Заполнить буфер, чтобы из него можно было получить байт НА ПОЗИЦИИ target
	 * Обязанность вызывающего следить чтобы выполнялось target &lt; buf.length
	 */
	protected void bufferize(int target) throws IOException
	{
		if(is != null)
		{
			while(readpos <= target)
			{
				buf[readpos++] = (byte)is.read();
			}
		}
	}
}
