/*
 *  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.ui;

import java.io.File;

import at.ac.tuwien.ims.util.ExtractionTask;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.control.Slider;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;

/**
 * Panel that displays all the extracted Keyframes
 * Keyframes get extracted in a separate Thread to keep the UI from freezing up
 * @author Andreas Waltenberger
 *
 */
public class KeyframePane extends VBox {

	private Text keyframesText;
	private VBox thumbnails;
	private Notification notification;
	private ExtractionTask extraction;
	private ProgressBar progress;
	private Slider ruler;
	private Label label;
	private VBox progressLayout;
	private HBox labelCancel;
	private Button cancel;
	private ScrollPane keyframePane;
	private ScrollPane controlPane;

	public KeyframePane() {
		setMinWidth(250);
		setMaxWidth(400);
		
		// show extraction progress and ruler after extraction
		controlPane = new ScrollPane();
		controlPane.setMinSize(250, 20);
		controlPane.setHbarPolicy(ScrollBarPolicy.NEVER);
		controlPane.setVbarPolicy(ScrollBarPolicy.NEVER);
		
		keyframesText = new Text("Open a\nVideo");
		keyframesText.setFont(Font.font("Helvetica", FontWeight.BOLD, 40));
		keyframesText.setFill(Color.gray(0.35));
		keyframesText.setTextAlignment(TextAlignment.CENTER);
		keyframesText.translateXProperty().bind(widthProperty().subtract(keyframesText.getLayoutBounds().getWidth()).divide(2));
		keyframesText.translateYProperty().bind(heightProperty().subtract(keyframesText.getLayoutBounds().getHeight()).divide(2).subtract(controlPane.heightProperty()));
		
		// displays a ruler to slim down the number of shown Keyframes
		ruler = new Slider();
		ruler.setMin(5);
		ruler.setMajorTickUnit(10);
		ruler.setMinorTickCount(0);
		ruler.setBlockIncrement(10);
		ruler.setSnapToTicks(true);
		ruler.setShowTickLabels(true);
		ruler.setShowTickMarks(true);
		ruler.prefWidthProperty().bind(widthProperty().subtract(10));
		ruler.setTranslateY(15);
		ruler.valueProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				if(!ruler.isValueChanging()) {
					displayFrames((float) ruler.getValue());
				}
			}
		});

		progress = new ProgressBar();
		progress.setPrefSize(200, 20);
		
		label = new Label("Extracting Keyframes");
		label.setPrefSize(176, 20);

		cancel = new Button("X");
		cancel.setFont(Font.font("Arial", FontWeight.BLACK, 11));
		cancel.setPrefSize(20, 13);
		cancel.setOnAction(new EventHandler<ActionEvent>() {
			@Override
			public void handle(ActionEvent event) {
				extraction.cancel();
			}
		});

		labelCancel = new HBox();
		labelCancel.getChildren().addAll(label, cancel);
		
		progressLayout = new VBox(1);
		progressLayout.getChildren().addAll(progress, labelCancel);
		progressLayout.translateXProperty().bind(widthProperty().subtract(progress.getPrefWidth()).divide(2));
		progressLayout.setTranslateY(7);

		keyframePane = new ScrollPane();
		keyframePane.setHbarPolicy(ScrollBarPolicy.NEVER);
		keyframePane.prefHeightProperty().bind(heightProperty().subtract(controlPane.minHeightProperty()));
		keyframePane.setContent(keyframesText);
		
		notification = new Notification("Please use a valid video file.");
		
		getChildren().addAll(controlPane, keyframePane);
	}

	// function to calculate which frames to show and which to hide after a ruler update
	private void displayFrames(float value) {
		int max = thumbnails.getChildren().size();
		int shown = max;
		
		// for the upper half of the selection, calculate which frames to remove
		if(value >= max / 2) {
			int remove = Math.round(max / (max-value));
			
			for(int i = 0; i < max; i++) {
				if(i % remove == 0) {
					thumbnails.getChildren().get(i).setVisible(false);
					shown--;
					if(shown == value) {
						break;
					}
				} else {
					thumbnails.getChildren().get(i).setVisible(true);
				}
			}
		// for the lower half, calculate which to keep
		} else {
			int show = Math.round(max / value);
			
			for(int i = 0; i < max; i++) {
				if(i % show == 0) {
					thumbnails.getChildren().get(i).setVisible(true);
				} else {
					thumbnails.getChildren().get(i).setVisible(false);
					shown--;
					if(shown == value) {
						break;
					}
				}
			}
		}
	}

	// function that starts the extraction progress
	public void openVideo(File video) {
		thumbnails = new VBox(3);
		extraction = new ExtractionTask(video, this, thumbnails);
		// bind the progressbar to the progress of the extraction task
		progress.progressProperty().bind(extraction.progressProperty());
		
		// while the extraction is running ...
		extraction.setOnRunning(new EventHandler<WorkerStateEvent>() {
			@Override
			public void handle(WorkerStateEvent event) {
				// ... show the progressbar and display already extracted Keyframes
				controlPane.setContent(progressLayout);
				keyframePane.setContent(thumbnails);
			}
		});

		// when extraction is done ...
		extraction.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
			@Override
			public void handle(WorkerStateEvent event) {
				// replace the progressbar with the ruler
				ruler.setMax(extraction.getValue().size());
				ruler.setValue(extraction.getValue().size());
				controlPane.setContent(ruler);
			}
		});

		// if the extraction gets cancelled reset the Panel to its default state
		extraction.setOnCancelled(new EventHandler<WorkerStateEvent>() {
			@Override
			public void handle(WorkerStateEvent event) {
				controlPane.setContent(null);
				keyframePane.setContent(keyframesText);
			}
		});

		// if an Exception occurs (File is not a valid video) the Panel gets reset and the application displays a notification
		extraction.setOnFailed(new EventHandler<WorkerStateEvent>() {
			@Override
			public void handle(WorkerStateEvent event) {
				controlPane.setContent(null);
				keyframePane.setContent(keyframesText);
				notification.showAndWait();
				extraction.getException().printStackTrace();
			}
		});
		Thread thread = new Thread(extraction);
		thread.setDaemon(true);
		thread.start();
	}
}
