package com.one;

import java.io.*;
import java.util.Hashtable;
import com.vmx.*;

/**
 * Класс для работы с PAK файлами.
 */
public class PakFile
{
	protected RandomAccessInputStream bis;
	protected ByteArrayOutputStream baos_header, baos_files;
	protected DataOutputStream dos_header;
	
	protected short currindex = 0;
	protected int curroffset = 0;
	protected byte[] buf = null;
	
	protected short header;      // размер заголовка (смещение первого файла)
	protected short count;       // число файлов
	protected String[] names;    // имя файла
	protected int[] offset;      // смещение файла относительно конца заголовка
	protected short[] size;      // размер файла
	protected Hashtable entries; // таблица соответствия индексов именам
	
	/**
	 * Открытие PAK файла из потока в режиме чтения
	 */
	public PakFile(RandomAccessInputStream is) throws IOException
	{
		closeOutput();
		
		//bis = new BufferedInputStream(is);
		bis = is;
		BufDataInputStream dis = new BufDataInputStream(bis);
		
		header = dis.readShort();
		count = dis.readShort();
		
		names = new String[count];
		offset = new int[count];
		size = new short[count];
		
		entries = new Hashtable(count + count / 2);
		
		for(int i = 0; i < count; i++)
		{
			names[i] = dis.readUTF();
			offset[i] = dis.readInt(); // + header;
			size[i] = dis.readShort();
			
			entries.put(names[i], new Integer(i));
		}
	}
	
	/**
	 * Создание PAK файла в режиме записи.
	 *
	 * @param newcount число элементов
	 */
	public PakFile(int newcount) throws IOException
	{
		closeInput();
		
		baos_header = new ByteArrayOutputStream();
		dos_header = new DataOutputStream(baos_header);
		baos_files = new ByteArrayOutputStream();
		buf = new byte[AuxClass.COPYBUFSIZE];
		currindex = 0;
		curroffset = 0;
		
		header = 0;
		count = (short)newcount;
		
		dos_header.writeShort(header);
		dos_header.writeShort(count);
		
		names = new String[count];
		offset = new int[count];
		size = new short[count];
		
		entries = null;
	}
	
	protected void closeInput() throws IOException
	{
		if(bis != null)
		{
			bis.close();
			bis = null;
		}
			
		names = null;
		offset = null;
		size = null;
		entries = null;
	}
	
	protected void closeOutput() throws IOException
	{
		if(dos_header != null)
		{
			dos_header.close();
			dos_header = null;
			baos_header = null;
		}
		
		if(baos_files != null)
		{
			baos_files.close();
			baos_files = null;
		}
		
		buf = null;
		System.gc();
	}
	
	public void close() throws IOException
	{
		closeInput();
		closeOutput();
	}
	
	public String[] listElements()
	{
		if(names != null)
		{
			String[] s = new String[names.length];
			System.arraycopy(names, 0, s, 0, names.length);
			
			return s;
		}
		else
		{
			return null;
		}
	}
	
	public InputStream getElementAsStream(String name) throws IOException, IllegalStateException
	{
		if(bis == null)
		{
			throw new IllegalStateException("Pak file was not open for reading");
		}
		
		Integer index = (Integer)entries.get(name);
		
		if(index == null)
		{
			throw new IOException("File \"" + name + "\" not found");
		}
		
		return new PartialInputStream(bis, offset[index.intValue()] + header, size[index.intValue()]);
	}
	
	public short getElementSize(String name) throws IOException, IllegalStateException
	{
		if(bis == null)
		{
			throw new IllegalStateException("Pak file was not open for reading");
		}
		
		Integer index = (Integer)entries.get(name);
		
		if(index == null)
		{
			throw new IOException("File \"" + name + "\" not found");
		}
		
		return size[index.intValue()];
	}
	
	public void addElement(String name, InputStream is, ProgressCallback callback) throws IOException, IllegalStateException
	{
		if(baos_header == null || baos_files == null)
		{
			throw new IllegalStateException("Pak file was not open for writing");
		}
		else if(currindex >= count)
		{
			throw new IllegalStateException("All available elements have been set");
		}
		
		dos_header.writeUTF(name);
		dos_header.writeInt(curroffset);
		dos_header.writeShort(is.available());
		
		curroffset += is.available();
		currindex++;
		
		if(callback != null)
		{
			int buflen;
			
			while(is.available() > 0)
			{
				baos_files.write(buf, 0, buflen = is.read(buf));
				callback.progress(buflen);
			}
		}
		else
		{
			while(is.available() > 0)
			{
				baos_files.write(buf, 0, is.read(buf));
			}
		}
	}
	
	public void write(OutputStream os) throws IOException, IllegalStateException
	{
		if(baos_header == null || baos_files == null)
		{
			throw new IllegalStateException("Pak file was not open for writing");
		}
		
		buf = baos_header.toByteArray();
		
		header = (short)buf.length;
		buf[0] = (byte)((header >> 8) & 0xFF);
		buf[1] = (byte)(header & 0xFF);
		
		os.write(buf);
		os.write(baos_files.toByteArray());
		os.flush();
	}
	
//	private static void out(String s)
//	{
//		System.out.println("[PakFile] " + s);
//	}
}
