package com.srbenoit.media.movie;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
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>BufferedImageDataSource</code>.
 */
public class BufferedImageSourceStream implements PullBufferStream {

    /** the list of image filenames */
    private final transient List<BufferedImage> 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>BufferedImageSourceStream</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 BufferedImageSourceStream(final int width, final int height, final int frameRate,
        final List<BufferedImage> 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;
    }

    /**
     * This is 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 {

        BufferedImage imageFile;
        ByteArrayOutputStream baos;
        byte[] frameData;

        // 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;
        } else {

            imageFile = this.images.get(this.nextImage);
            this.nextImage++;

            if (imageFile == null) {

                // We are done. Set EndOfMedia.
                buf.setEOM(true);
                buf.setOffset(0);
                buf.setLength(0);
                this.ended = true;

                return;
            }

            baos = new ByteArrayOutputStream();
            ImageIO.write(imageFile, "JPEG", baos);
            baos.close();

            frameData = baos.toByteArray();

            buf.setData(frameData);
            buf.setOffset(0);
            buf.setLength(frameData.length);
            buf.setFormat(this.format);
            buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
        }
    }

    /**
     * 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  true if stream has ended; false 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;
    }
}
