// Copyright 2001, FreeHEP.
package org.freehep.graphicsio.swf;

import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.InputStream;
import java.io.IOException;
import java.util.Vector;

import org.freehep.util.io.ActionHeader;
import org.freehep.util.io.ActionSet;
import org.freehep.util.io.TagHeader;
import org.freehep.util.io.TagSet;
import org.freehep.util.io.TaggedInputStream;

/**
 * This class extends the TaggedInputStream with several methods
 * to read SWF primitives from the stream and to read TagHeaders.
 * It also handles the management of the SWFDictionary.
 *
 * @author Mark Donszelmann
 * @author Charles Loomis
 * @version $Id: SWFInputStream.java,v 1.1.1.1 2004/05/31 05:08:51 kuromaru Exp $
 */
public class SWFInputStream extends TaggedInputStream implements SWFConstants {

    private SWFDictionary dictionary;
    private byte[] jpegTable;

    public SWFInputStream(InputStream is)
        throws IOException {

        this(is, DEFAULT_VERSION);
    }

    public SWFInputStream(InputStream is, int version)
        throws IOException {

        this(is, new SWFTagSet(version), new SWFActionSet(version));
    }

    public SWFInputStream(InputStream is, SWFSpriteTagSet tagSet, SWFActionSet actionSet)
        throws IOException {

        // SWF is little-endian
        super(is, tagSet, actionSet, true);

        dictionary = new SWFDictionary();
    }

    /**
     * Read a fixed point value (16.16). */
    public float readFixed()
        throws IOException {

        byteAlign();
        int frac = readUnsignedByte();
        frac |= readUnsignedByte() << 8;
        int whole = readUnsignedByte();
        whole |= readUnsignedByte() << 8;
        return ((float)whole)+((float)frac)/((float) 0x10000);
    }

    /**
     * Read a rectangle from the stream. */
    public Rectangle2D readRect()
        throws IOException {

        byteAlign();
        int nbits = (int)readUBits(5);
        int xmin = (int)readSBits(nbits);
        int xmax = (int)readSBits(nbits);
        int ymin = (int)readSBits(nbits);
        int ymax = (int)readSBits(nbits);
        return new Rectangle2D.Double(xmin/TWIPS, ymin/TWIPS, (xmax-xmin)/TWIPS, (ymax-ymin)/TWIPS);
    }

    /**
     * Read an RGB value from the stream. */
    public Color readColor(boolean alpha)
        throws IOException {
        int r = readUnsignedByte();
        int g = readUnsignedByte();
        int b = readUnsignedByte();
        int a = (alpha) ? readUnsignedByte() : 255;
        return new Color(r,g,b,a);
    }

    /**
     * Read a matrix from the stream. */
    public AffineTransform readMatrix()
        throws IOException {

        byteAlign();

        // Set default values.
        float sx = 1.f;
        float sy = 1.f;
        float kx = 0.f;
        float ky = 0.f;
        float tx = 0.f;
        float ty = 0.f;

        // Get the scale bits.
        if (readBitFlag()) {
            int nbits = (int)readUBits(5);
            sx = readFBits(nbits);
            sy = readFBits(nbits);
        }

        // Rotate or skew values.
        if (readBitFlag()) {
            int nbits = (int)readUBits(5);
            kx = readFBits(nbits);
            ky = readFBits(nbits);
        }

        // Translation values.
        int nbits = (int)readUBits(5);
        tx = (float)readSBits(nbits)/TWIPS;
        ty = (float)readSBits(nbits)/TWIPS;

        return new AffineTransform(sx,ky,kx,sy,tx,ty);
    }

    /**
     * Read a string from the stream. */
    public String readString()
            throws IOException {
        if (getVersion() >= 6) {
            String s = readUTF();
            readByte();
            return s;
        } else {
            return readAsciiZString();
        }
    }

    public int readLanguageCode() throws IOException {
        return readUnsignedByte();
    }

    protected TagHeader readTagHeader() throws IOException {
        // Read the tag.
        byteAlign();
        int temp = read();
        // End of stream
        if (temp == -1) return null;

        temp |= readUnsignedByte() << 8;
        int tagID = temp >> 6;
        long length = temp & FIELD_MASK[5];
        if (length==0x3f) length = readUnsignedInt();
        return new TagHeader(tagID, length);
    }

    protected ActionHeader readActionHeader() throws IOException {

        int actionCode = readUnsignedByte();
        if (actionCode == 0) return null;

        int length = 0;
        if ((actionCode & 0x80) > 0) {
            length = readUnsignedShort();
        }

        return new ActionHeader(actionCode, length);
    }

    private SWFHeader header;

    public SWFHeader readHeader() throws IOException {
        if (header == null) {
            header = new SWFHeader(this);
        }
        return header;
    }

    public SWFDictionary getDictionary() {
        return dictionary;
    }

    public void setJPEGTable(byte[] table) {
        jpegTable = table;
    }

    public byte[] getJPEGTable() {
        return jpegTable;
    }

    public int getVersion() {
        return ((SWFSpriteTagSet)tagSet).getVersion();
    }
}

