/**
 * This package provides classes to handle video in Processing. The API is compatible with the built-in video library of Processing. 
 * GSVideo uses the multimedia toolkit GStreamer (http://www.gstreamer.net/)  through the gstreamer-java bindings by Wayne Meissener:
 * http://code.google.com/p/gstreamer-java/ 
 * @author Andres Colubri
 * @version 0.4.5
 *
 * Copyright (c) 2008 Andres Colubri
 *
 * This source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This code is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License is available on the World
 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also
 * obtain it by writing to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package codeanticode.gsvideo;

import processing.core.*;
import java.nio.*;
import org.gstreamer.*;
import org.gstreamer.elements.*;
import org.gstreamer.Buffer;

/**
 * This class makes movies from a running program. NOT WORKING YET.
 */
public class GSMovieMaker 
{
    int movieWidth;
    int movieHeight;
  
    AppSrc buffersrc;
    
    
    byte[] moviePixels;
  
    Pipeline gpipe;
  
    private PApplet parent;
    int width, height;
  
    public static final int THEORA = 0;  
    public static final int MPEG4 = 1;
/*
  public static final int WORST = 0;
  public static final int LOW = 1;
  public static final int MEDIUM = 2;
  public static final int HIGH = 3;
  public static final int BEST = 4;
  public static final int LOSSLESS = 5;
*/  
  
    int frameRate;
    int waitTime;

    public GSMovieMaker(PApplet parent, int requestWidth, int requestHeight, String filename) 
    {
        this(parent, requestWidth, requestHeight, filename, MPEG4, 30);
    }
  
    public GSMovieMaker(PApplet parent, int requestWidth, int requestHeight, String filename, int codecType, int ifps) 
    {
        this.parent = parent;
        //parent.super.init(parent.width, parent.height, PConstants.RGB);
	   	   
        GSVideo.init();	   
	  
        // register methods
        parent.registerDispose(this);
	
        frameRate = ifps;
        Double t = 1000.0 / ifps;
        waitTime = t.intValue();
	
        width = parent.width;
        height = parent.height;
	
        //synchQueue = new SynchronousQueue<ByteBuffer>();

        
        movieWidth = requestWidth;
        movieHeight = requestHeight;
  
        gpipe = new Pipeline("GSMovieMaker");	  
	
        //final GSBufferSrc buffersrc = new GSBufferSrc(parent, synchQueue, waitTime, movieWidth, movieHeight, "pixelbuffer source");
        
        buffersrc = (AppSrc)ElementFactory.make("appsrc", "buffersrc");
        buffersrc.setMaxBytes(movieWidth * movieHeight * 4 * 30);
        buffersrc.setSize(movieWidth * movieHeight * 4 * 30);
        
        
        //final Element capsfilter0 = ElementFactory.make("capsfilter", "srcfilter0");
        Caps srccaps = new Caps("video/x-raw-rgb, width=" + movieWidth + ", height=" + movieHeight + ", bpp=32, depth=24, framerate=" + frameRate + "/1");
        //capsfilter0.setCaps(srccaps);
        buffersrc.setCaps(srccaps);
        
        
        final Element conv = ElementFactory.make("ffmpegcolorspace", "colorconverter");
        
        final Element capsfilter = ElementFactory.make("capsfilter", "srcfilter");
        Caps fltcaps = new Caps("video/x-raw-yuv, width=" + movieWidth + ", height=" + movieHeight + ", bpp=32, depth=24, framerate=" + frameRate + "/1");
        capsfilter.setCaps(fltcaps);
	
        //final Element videorate = ElementFactory.make("videorate", "videorate");
        //final Element ratefilter = ElementFactory.make("capsfilter", "RateFilter");
        //ratefilter.setCaps(Caps.fromString("video/x-raw-rgb, framerate=" + frameRate + "/1"));
	
        
	
        final Element videoenc;
        final Element muxer;
        if (codecType == THEORA) 
        {
            videoenc = ElementFactory.make("theoraenc",  "theoraenc");
            muxer = ElementFactory.make("oggmux", "oggmux");
        }
        else 
        {
            videoenc = ElementFactory.make("ffenc_mpeg4",  "ffenc_mpeg4");
            muxer = ElementFactory.make("avimux", "avimux");	
        }
	
        Element videosink = ElementFactory.make("filesink", "sink");
        videosink.set("location", parent.savePath(filename));
	       
        gpipe.addMany(buffersrc, conv, capsfilter, videoenc, muxer, videosink);
        Element.linkMany(buffersrc, conv, capsfilter, videoenc, muxer, videosink);

        //Element videosink = ElementFactory.make("fakesink", "videosink");
        //gpipe.addMany(buffersrc, conv, capsfilter, videosink);
        //Element.linkMany(buffersrc, conv, capsfilter, videosink);
        
        
        gpipe.setState(State.PLAYING);
        
        // Creating bus to handle end-of-stream event.
        
        /*
        buffersrc.getBus().connect(
            new Bus.
            {
                public void endOfStream(GstObject element) 
                { 
                    eosEvent();
                }
            }
        );
        */
        
    }
  
    public void addFrame() 
    {
        parent.loadPixels();
	
        // Converting int[] pixels array into byte array.
        //tmpBuffer.rewind();
        //tmpBuffer.asIntBuffer().put(parent.pixels);	
        
        Buffer srcBuffer = new Buffer(width * height * 4);
        Caps buffcaps = new Caps("video/x-raw-rgb, width=" + movieWidth + ", height=" + movieHeight + ", bpp=32, depth=24, framerate=" + frameRate + "/1");
        srcBuffer.setCaps(buffcaps);

        ByteBuffer tmpBuffer = srcBuffer.getByteBuffer();
        tmpBuffer.clear();
        tmpBuffer.asIntBuffer().put(parent.pixels);
        //srcBuffer.setOffset(0);
        //srcBuffer.setDuration(ClockTime.fromNanos(NANOS_PER_SECOND / 30));
        //srcBuffer.setTimestamp(ClockTime.fromNanos(parent.millis() * NANOS_PER_SECOND / 1000));
        
        
        buffersrc.pushBuffer(srcBuffer);
        srcBuffer.dispose();

        System.out.println("maxSize: " + buffersrc.getSize());        
        System.out.println("maxBytes: " + buffersrc.getMaxBytes());
        System.out.println("Done.");
        
        /*
        try
        {	
            parent.print("Offering buffer...");
            synchQueue.offer(tmpBuffer, waitTime, TimeUnit.MILLISECONDS);
            parent.println("Sent buffer.");
        }
        catch (InterruptedException ie)
        {
            ie.printStackTrace();
        }*/
        
    }

    public void restart() 
    { 
        gpipe.setState(State.PLAYING);
    }
  
    public void finish() 
    {
        gpipe.setState(State.PAUSED);
        buffersrc.endOfStream();
        System.out.println("END OF STREAM");
    }
   
    public void dispose() 
    {
    } 
  
    static long NANOS_PER_SECOND = 1000000000;
}
