/*
 *  Keyframe-Based Video Summarization Designer
 *  Copyright (C) 2014 Waltenberger Andreas
 *
 *  This program 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 program 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.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package at.ac.tuwien.ims.util;

import static org.bytedeco.javacpp.opencv_core.cvAbsDiff;
import static org.bytedeco.javacpp.opencv_core.cvSum;
import static org.bytedeco.javacpp.opencv_imgproc.CV_BGR2GRAY;
import static org.bytedeco.javacpp.opencv_imgproc.cvCvtColor;

import java.io.File;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.layout.VBox;

import org.bytedeco.javacpp.opencv_core.CvScalar;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacv.FFmpegFrameGrabber;

import at.ac.tuwien.ims.layout.Keyframe;
import at.ac.tuwien.ims.ui.KeyframePane;

/**
 * Task to extract Keyframes in a non-blocking fashion so the UI stays responsive
 * @author Andreas Waltenberger
 *
 */
public class ExtractionTask extends Task<ObservableList<Keyframe>> {

	private File video;
	private KeyframePane pane;
	private VBox thumbnails;

	public ExtractionTask(File video, KeyframePane pane, VBox thumbnails) {
		this.video = video;
		this.pane = pane;
		this.thumbnails = thumbnails;
	}

	@Override
	protected ObservableList<Keyframe> call() throws Exception {
		ObservableList<Keyframe> results = FXCollections.observableArrayList();

		FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(video);
		grabber.start();

		int frame = 1;
		// maximum number of frames we are extracting is 100
		int maxFrames = 100;
		double norm, distance;

		// calculate at which rate we have to take frames to get 100
		int samplingRate = Math.max(Math.round((float) grabber.getFrameRate()), grabber.getLengthInFrames() / maxFrames);

		IplImage i = grabber.grab();
		// norm-factor used for calculating information content of an image
		norm = 256 * i.height() * i.width();
		// convert the grabbed image to grayscale
		IplImage i_gray = IplImage.create(i.cvSize(), i.depth(), 1);
		cvCvtColor(i, i_gray, CV_BGR2GRAY);

		// allocate space for future frames and a difference-frame for calculation
		IplImage j = IplImage.create(i.cvSize(), i.depth(), i.nChannels());
		IplImage j_gray = IplImage.create(j.cvSize(), j.depth(), 1);

		IplImage diff_gray = IplImage.create(i_gray.cvSize(), i_gray.depth(), i_gray.nChannels());

		CvScalar diff_gray_sum;

		while ((j = grabber.grab()) != null) {
			if (isCancelled()) {
				return null;
			} else if (frame % samplingRate == 0) {
				// convert the newly grabbed frame to grayscale
				cvCvtColor(j, j_gray, CV_BGR2GRAY);
				// compare the 2 grabbed frames
				cvAbsDiff(i_gray, j_gray, diff_gray);
				diff_gray_sum = cvSum(diff_gray);

				// norm the information value
				distance = diff_gray_sum.blue() / norm;
				// test if we met the distance threshold
				if (distance >= 0.1) {
					// save the frame if we did
					final Keyframe keyframe = new Keyframe(j, pane);
					results.add(keyframe);
					Platform.runLater(new Runnable() {
						@Override
						public void run() {
							// if the Frame goes invisible (Ruler-change) make it also disappear
							keyframe.managedProperty().bind(keyframe.visibleProperty());
							thumbnails.getChildren().add(keyframe);
						}
					});
				} else {
					// if the frame was too similar we discard it which results in one less extracted frame
					maxFrames--;
				}

				// update the progressbar
				updateProgress(results.size(), maxFrames);
				// save the second grayscale image for the next comparison
				i_gray.asCvMat().put(j_gray.asCvMat());
			}
			frame++;
		}

		return results;
	}
}
