package zall.transcoder;

import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.servlet.ServletConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;

import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.MediaToolAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.AudioSamplesEvent;
import com.xuggle.mediatool.event.IAddStreamEvent;
import com.xuggle.mediatool.event.IAudioSamplesEvent;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.mediatool.event.VideoPictureEvent;
import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;

import zall.Zallery;
import zall.bean.Media.Size;
import zall.bean.Video;
import zutil.StringUtil;
import zutil.db.DBConnection;
import zutil.log.LogUtil;

@WebServlet(value = "/transcoder", loadOnStartup = 100)
public class ZalleryTranscoder extends HttpServlet{
	private static final Logger logger = LogUtil.getLogger();
	private static final long serialVersionUID = 1L;

	// Media Queue
	private static Queue<Video> transcodingQueue;
	private static TranscoderThread worker;

	public void init( ServletConfig config ){
		try{
			transcodingQueue = new LinkedList<>();
			worker = new TranscoderThread();
			worker.start();

			// get not transcoded videos
			DBConnection db = null;
			try {
				db = Zallery.getDB();
				List<Video> incomplete = Video.loadUntransoded( db );
				synchronized (transcodingQueue) {
					transcodingQueue.addAll( incomplete );
					transcodingQueue.notify();
				}				
			} catch (Exception e) {
				logger.log(Level.SEVERE, null, e);
			}finally{
				if( db != null ) db.close();
			}
		}
		catch(Exception e){
			logger.log(Level.SEVERE, "Unable to initialize ZalleryTranscoder!", e);
		}
	}

	public void destroy( ){
		worker.abort();
	}


	public static Video getProcessingVideo(){
		return worker.currentVideo;
	}

	public static Queue<Video> getQueue(){
		return transcodingQueue;
	}


	/**
	 * Add a video to the transcoding queue
	 * 
	 * @param 		video		is the video to transcode
	 */
	public static void addVideo(Video video) {
		if( transcodingQueue == null ){
			logger.severe("ZalleryTranscoder not initialized!");
			return;
		}
		if( !transcodingQueue.contains(video) && worker.currentVideo != video ){
			synchronized (transcodingQueue){
				transcodingQueue.add( video );
				transcodingQueue.notify();
			}
		}
	}

	/**
	 * Is the class that is doing the actual work of transcoding videos
	 * 
	 * @author Ziver
	 */
	private static class TranscoderThread extends Thread{
		private boolean stop;
		private long startTime;
		private Video currentVideo;


		public void run(){
			logger.info("Transcoder thread started.");
			try {
				while( !stop ){
					// Get video to transcode
					while( !transcodingQueue.isEmpty() ){
						currentVideo = transcodingQueue.poll();
						startTime = System.currentTimeMillis();
						logger.info("Starting transcoding video(id:"+currentVideo.getId()+")");

						File originalFile = currentVideo.getFile( Size.ORIGINAL );
						File mediumFile = currentVideo.getFile( Size.MEDIUM );
						File smallFile = currentVideo.getFile( Size.SMALL );
			        
						///////////// Start Transcoding
						// create a media reader
						IMediaReader reader = ToolFactory.makeReader( originalFile.getPath() );
						reader.addListener(new FrameGrabListener(reader, smallFile, 0.2));
				        reader.addListener(new ProgressListener(reader));
				        
						VideoTranscoderListener converter = new VideoTranscoderListener(640, 360);
						reader.addListener(converter);
						
						// create a media writer
						IMediaWriter writer = ToolFactory.makeWriter(mediumFile.getPath(), reader);
						converter.addListener(writer);

						// create a media viewer with stats enabled for debugging
						// add a viewer to the reader, to see the decoded media
						//IMediaViewer mediaViewer = ToolFactory.makeViewer(true);
						//reader.addListener(mediaViewer);
						//writer.addListener(mediaViewer);

						// read and decode packets from the source file and
						// and dispatch decoded audio and video to the writer
						while (reader.readPacket() == null);

						// Incomplete transcoding
						if( reader.isOpen() ){
							logger.severe("Transcoding incomplete, removing incomplete files!");
							reader.close();
							mediumFile.delete();
							smallFile.delete();
							if( stop )
								return;
						}
						else{
							logger.info("Done transcoding video(id:"+currentVideo.getId()+") time: "+StringUtil.formatTimeToString(System.currentTimeMillis()-startTime));
							currentVideo.setTranscoded(true);
							try{
								DBConnection db = Zallery.getDB();
								currentVideo.save(db);
								db.close();
							}catch(Exception e){
								logger.log(Level.SEVERE, "Unable to save video bean!", e);
							}
						}
						currentVideo = null;
					}
					// Wait for new video to transcode
					synchronized (transcodingQueue) {
						transcodingQueue.wait();
					}
				}

                logger.info("Transcoding thread has stopped!");
			} catch (Exception e) {
				logger.log(Level.SEVERE, "Transcoding thread has crashed!", e);
			}
		}


		public void abort(){
			stop = true;
			synchronized (transcodingQueue) {
				transcodingQueue.notifyAll();
			}
		}
	}
}


