package com.srbenoit.media.movie;

import java.awt.Dimension;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;
import javax.media.Buffer;
import javax.media.Format;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.PullBufferStream;

/**
 * The source stream to go along with <code>ImageDataSource</code>.
 */
public class ImageSourceStream implements PullBufferStream {

    /** the list of image filenames */
    private final transient List<String> images;

    /** the target video format */
    private final transient VideoFormat format;

    /** the index of the next image to be read */
    private transient int nextImage = 0;

    /** true if we are finished */
    private transient boolean ended = false;

    /**
     * Constructs a new <code>ImageSourceStream</code>.
     *
     * @param  width      the target frame width
     * @param  height     the target frame height
     * @param  frameRate  the target frame rate
     * @param  imageList  the list of image filenames
     */
    public ImageSourceStream(final int width, final int height, final int frameRate,
        final List<String> imageList) {

        this.images = imageList;

        this.format = new VideoFormat(VideoFormat.JPEG, new Dimension(width, height),
                Format.NOT_SPECIFIED, Format.byteArray, frameRate);
    }

    /**
     * We should never need to block assuming data are read from files.
     *
     * @return  <code>false</code>
     */
    public boolean willReadBlock() {

        return false;
    }

    /**
     * Called from the Processor to read a frame worth of video data.
     *
     * @param   buf  the buffer in which to store the data
     * @throws  IOException  if there is an error reading the data
     */
    public void read(final Buffer buf) throws IOException {

        String imageFile;
        RandomAccessFile raFile;
        byte[] data;

        // Check if we've finished all the frames.
        if (this.nextImage >= this.images.size()) {

            // We are done. Set EndOfMedia.
            buf.setEOM(true);
            buf.setOffset(0);
            buf.setLength(0);
            this.ended = true;

            return;
        }

        imageFile = this.images.get(this.nextImage);
        this.nextImage++;

        // Open a random access file for the next image.
        raFile = new RandomAccessFile(imageFile, "r");

        // Check the input buffer type & size.
        if (buf.getData() instanceof byte[]) {
            data = (byte[]) buf.getData();

            // Check to see the given buffer is big enough for the frame.
            if ((data == null) || (data.length < raFile.length())) {
                data = new byte[(int) raFile.length()];
                buf.setData(data);
            }

            // Read the entire JPEG image from the file.
            raFile.readFully(data, 0, (int) raFile.length());

            buf.setOffset(0);
            buf.setLength((int) raFile.length());
            buf.setFormat(this.format);
            buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
        }

        // Close the random access file.
        raFile.close();
    }

    /**
     * Returns the format of each video frame. That will be JPEG.
     *
     * @return  the format
     */
    public Format getFormat() {

        return this.format;
    }

    /**
     * Gets the content descriptor.
     *
     * @return  the content type (RAW)
     */
    public ContentDescriptor getContentDescriptor() {

        return new ContentDescriptor(ContentDescriptor.RAW);
    }

    /**
     * Gets the content length.
     *
     * @return  0
     */
    public long getContentLength() {

        return 0;
    }

    /**
     * Tests whether the stream has ended.
     *
     * @return  <code>true</code> if stream has ended; <code>false</code> otherwise.
     */
    public boolean endOfStream() {

        return this.ended;
    }

    /**
     * Dummy implementation of the superclass method.
     *
     * @return  a zero-length object list
     */
    public Object[] getControls() {

        return new Object[0];
    }

    /**
     * Dummy implementation of the superclass method.
     *
     * @param   type  the type of control to get
     * @return  <code>null</code>
     */
    public Object getControl(final String type) {

        return null;
    }
}
