/**
 * @(#)SWFInputStream.java
 * @author  Intelligent Technology Inc.
 * @version 1.00 2002/4
 */
package	jp.co.iti.swf;

import	java.io.*;
import	java.util.zip.*;
import	jp.co.iti.fagot.util.*;

/**
 * SWFファイルを開くためのファイルストリーム。
 */
class SWFInputStream extends FilterInputStream{
	private	static	final	int	STRING_BUFFER_SIZE	=	0xff;
	private int ver;
	
	/**
	 * @since	1.00
	 */
	public	SWFInputStream(InputStream instream){ super(instream); }
	
	/**
	 * @param	name	ファイル名
	 * @since	1.00
	 */
	public	SWFInputStream(String name) throws FileNotFoundException {
		super(new FileInputStream(name));
	}
	
	/**
	 * 1バイトのデータを読み込んで符号なし8bit値をint型で返します。
	 * @return	符号なし8bit値として読み込まれた値
	 * @since	1.00
	 */
	public int readUI8() throws IOException{
		int ch = in.read();
		if (ch < 0) throw new EOFException();
		return ch;
    }
	
	/**
	 * 2バイトのデータを読み込んで符号なし16bit値をint型で返します。
	 * @return	符号なし16bit値として読み込まれた値
	 * @since	1.00
	 */
	public	int readUI16() throws IOException{
		int ch1 = in.read();
		int ch2 = in.read();
		if ((ch1 | ch2) < 0) throw new EOFException();
		return ch1 | (ch2 << 8);
//		return (ch1 << 8) + ch2;
	}
	
	/**
	 * 4バイトのデータを読み込んで符号なし32bit値をlong型で返します。
	 * @return	符号なし32bit値として読み込まれた値
	 * @since	1.00
	 */
	public	long readUI32() throws IOException{
		int ch1 = in.read();
		int ch2 = in.read();
		int ch3 = in.read();
		int ch4 = in.read();
		
		if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException();
		long result = ch1 | (ch2 << 8) | (ch3 << 16) | (((long)ch4) << 24);
		
		return result;
	}
	
	/**
	 * 2バイトのデータを読み込んで16bitのbit列として値をint型で返します。
	 * @return	16bitのbit列として読み込まれた値
	 * @since	1.00
	 */
	public	int readUB16() throws IOException{
		int ch1 = in.read();
		int ch2 = in.read();
		if ((ch1 | ch2) < 0) throw new EOFException();
		return (ch1 << 8) | ch2;
	}
	
	/**
	 * 0x00を終端とする文字列を読み込んでString型で返します。
	 * @return	読み込まれた文字列
	 * @since	1.00
	 */
	public	String readString() throws IOException{
		byte[] buf = new byte[STRING_BUFFER_SIZE];
		
		while(true){
			StringBuffer strbuf = new StringBuffer();
			for(int i=0;i<STRING_BUFFER_SIZE;i++){
				int ch = in.read();
				if (ch < 0) throw new EOFException();
				if(0x00 == ch){
					if(0<i)	strbuf.append(new String(buf).substring(0,i));
					return strbuf.toString();
				}
				buf[i] = (byte)ch;
			}
			strbuf.append(new String(buf));
		}
	}
	
	/**
	 * SWF Basic Types の rect を読む。
	 * @return	読んだ値をSWFRectとして返す。
	 * @since	1.00
	 */
	public	SWFRect readRect() throws IOException{
		SWFRect rect = new SWFRect();
		
		SWFBitInputFilter	bitfilter	=	new SWFBitInputFilter(this);
		
		int nBits	= bitfilter.read(SWFBitInputFilter.BYTE ,5);
//		int nBits	= bitfilter.read(SWFBitInputFilter.WORD ,5);
		rect.Xmin	= bitfilter.read(SWFBitInputFilter.WORD ,nBits);
		rect.Xmax	= bitfilter.read(SWFBitInputFilter.WORD ,nBits);
		rect.Ymin	= bitfilter.read(SWFBitInputFilter.WORD ,nBits);
		rect.Ymax	= bitfilter.read(SWFBitInputFilter.WORD ,nBits);
		
		return rect;
	}
	
	/**
	 * SWF Basic Types の matrix を読む。
	 * @return	読んだ値をSWFMatrixとして返す。
	 * @since	1.00
	 */
	public	SWFMatrix readMatrix() throws IOException{
		SWFMatrix			matrix		=	new SWFMatrix();
		SWFBitInputFilter	bitfilter	=	new SWFBitInputFilter(this);
		
		// Scale
		if(0x00 != bitfilter.read(SWFBitInputFilter.BYTE ,1)){	// hasScale
			matrix.hasScale	= true;
			int nScaleBits	= bitfilter.read(SWFBitInputFilter.BYTE ,5);
			matrix.scaleX	= bitfilter.read(SWFBitInputFilter.BYTE ,nScaleBits);
			matrix.scaleY	= bitfilter.read(SWFBitInputFilter.BYTE ,nScaleBits);
		}else	matrix.hasScale	= false;
		
		// Rotate
		if(0x00 != bitfilter.read(SWFBitInputFilter.BYTE ,1)){	// hasRotate
			matrix.hasRotate	= true;
			int nRotateBits		= bitfilter.read(SWFBitInputFilter.BYTE ,5);
			matrix.rotateSkew0	= bitfilter.read(SWFBitInputFilter.BYTE ,nRotateBits);
			matrix.rotateSkew1	= bitfilter.read(SWFBitInputFilter.BYTE ,nRotateBits);
		}else	matrix.hasRotate	= false;
		
		// Translate
		int nTranslateBits	= bitfilter.read(SWFBitInputFilter.BYTE ,5);
		matrix.translateX	= bitfilter.read(SWFBitInputFilter.BYTE ,nTranslateBits);
		matrix.translateY	= bitfilter.read(SWFBitInputFilter.BYTE ,nTranslateBits);
		
		return matrix;
	}
	
	/**
	 * SWF Basic Types の cxform を読む。
	 * @return	読んだ値をSWFCxformとして返す。
	 * @since	1.00
	 */
	public	SWFCxform readCxform(boolean withAlpha) throws IOException{
		SWFCxform			cxform		=	new SWFCxform();
		SWFBitInputFilter	bitfilter	=	new SWFBitInputFilter(this);
		
		cxform.hasAlpha = withAlpha;
		
		int	hasAdd		=	bitfilter.read(SWFBitInputFilter.BYTE ,1);
		int	hasMulti	=	bitfilter.read(SWFBitInputFilter.BYTE ,1);
		int	nBits		=	bitfilter.read(SWFBitInputFilter.BYTE ,4);
		
		// Multi
		if(0x00 != hasMulti){
			cxform.hasMulti		= true;
			cxform.RedMulti		= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			cxform.GreenMulti	= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			cxform.BlueMulti	= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			
			if(withAlpha) cxform.AlphaMulti = bitfilter.read(SWFBitInputFilter.BYTE ,nBits);	// alpha
		}else	cxform.hasMulti	= false;
		
		// Add
		if(0x00 != hasAdd){
			cxform.hasAdd	= true;
			cxform.RedAdd	= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			cxform.GreenAdd	= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			cxform.BlueAdd	= bitfilter.read(SWFBitInputFilter.BYTE ,nBits);
			
			if(withAlpha) cxform.AlphaAdd = bitfilter.read(SWFBitInputFilter.BYTE ,nBits);	// alpha
		}else	cxform.hasAdd	= false;
		
		return cxform;
	}
	
	/**
	 * SWFファイルのヘッダ情報を読む。
	 * @return	SWFファイルのヘッダ情報をSWFHeaderとして返す。
	 * @since	1.00
	 */
	public SWFHeader readHeader() throws SWFHeaderException{
		SWFHeader header = new SWFHeader();
		
		try{
			int ch	=	readUI8();				// 'F' or 'C'
			
//			if(0x46 != ch)	throw	new SWFHeaderException();
			switch(ch){
			case 0x46:	// 'F'
				header.isCompressed = false;
				break;
			case 0x43:	// 'C'
				header.isCompressed = true;
				break;
			default:
				throw	new SWFHeaderException();
			}
			
			ch		=	readUI8();				// 'W'
			if(0x57 != ch)	throw	new SWFHeaderException();
			ch		=	readUI8();				// 'S'
			if(0x53 != ch)	throw	new SWFHeaderException();
			
			header.Version = ver=	readUI8();	// Version
			header.FileLength	=	readUI32();	// FileLength
			
			if(header.isCompressed) in = new BufferedInputStream(new InflaterInputStream(in));
			
			header.FrameSize	=	readRect();	// FrameSize
			header.FrameRate	=	readUI16();	// FrameRate
			header.FrameCount	=	readUI16();	// FrameCount
			
		}catch(IOException e){ throw new SWFHeaderException(); }
		
		return header;
    }
	
	/**
	 * SWFファイルのタグ群を読む。タグ数が0の場合も空のSWFTagsを返す。
	 * @return	SWFファイルのタグ群をSWFTagsとして返す。
	 * @since	1.00
	 */
	public SWFTags readTags()
	throws
		 SWFBodyException ,SWFActionTagException ,SWFExportAssetsTagException
		,SWFImportAssetsTagException ,SWFActionRecordException ,SWFButtonTagException
		,SWFButton2TagException ,SWFActionRecordsException ,SWFButtonRecordsException
	{
		SWFTags tags = new SWFTags(ver);
		
		// タグヘッダリード
		try{
			while(true){
				int word	= readUI16();
				int code	= word >> 6;
				if(SWFTag.END == code){
					try{
						readUI8();
						ZDebug.trace("EndTagの後にdataあり!?");
					}catch(EOFException e){ }
					break;
				}
				
				long length	= word & 0x3f;
				
				if(0x3f == length){
					length = readUI32();	// long version
				}
				
				byte[] data = null;
				if(0 < length){
					data = new byte[(int)length];
					read(data);
				}
				
				try{ tags.add(code ,data); }
				catch(SWFException e){ e.printStackTrace(); }
			}
		}catch(IOException e){
			e.printStackTrace();
			throw new SWFBodyException();
		}
		
		return tags;
	}
}
