package project;

import java.io.File;
import java.net.URL;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Vector;

import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
import org.opencv.videoio.Videoio;

import Objects.DetectionAPP;
import Objects.DetectionArea;
import Objects.DetectionLine;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider;
import javafx.scene.control.TextArea;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

public class Controller implements Initializable {

	@FXML
	private Button playStopButton;
	
	@FXML
	private Button resetToDefaultButton;
	
	@FXML
	private ProgressBar progressBar;

	@FXML
	private Label timeLabel;

	@FXML
	public ImageView imgView;

	@FXML
	public Canvas canvas;

	@FXML
	public RadioButton radioButtonLine;

	@FXML
	public RadioButton radioButtonArea;

	@FXML
	public ToggleGroup groupModeLine;

	@FXML
	public ToggleGroup groupModeVideo;

	@FXML
	public RadioButton radioButtonStaticLine;
	
	@FXML
	public RadioButton radioButtonEveryWhere;

	@FXML
	public RadioButton radioButtonRGB;

	@FXML
	public RadioButton radioButtonBackground;
	
	@FXML
	public Slider dt_slider;
	
	@FXML
	public Slider noise_slider;
	
	@FXML
	public Slider traceLength_slider;
	
	@FXML
	public Slider skipedFrames_slider;
	
	@FXML
	public Slider distance_slider;
	
	@FXML
	public Slider maxArea_slider;
	
	@FXML
	public Slider minArea_slider;
	
	@FXML
	public Slider kernelSize_slider;
	
	@FXML
	public Slider learningRate_slider;
	
	@FXML
	public TextArea textArea;
	
	@FXML
	public MenuItem menuHelpControls;
	
	@FXML
	public MenuItem menuHelpAdvancedSetting;
	

	static boolean Continue = false;
	static boolean uriDone = false;
	private static final int pixelArea = 5;
	DetectionAPP app = new DetectionAPP();
	Vector<Point> pomocnePoints = new Vector<>();
	public static Image firstImage;
	static public String uri;
	GraphicsContext gc;
	Image currentImage;
	Image currenBackgroundImage;
	public static double frameCount = 0;
	static double frame = 0;
	public Detect detect;
	public Controller controller = this;

	public void update(Image finalImage, Image backgroungImage) {
		currentImage = finalImage;
		currenBackgroundImage = backgroungImage;

		if (radioButtonRGB.isSelected()) {
			canvas.setVisible(true);
			imgView.setVisible(false);
			gc.drawImage(finalImage, 0, 0);			
		} else if (radioButtonBackground.isSelected()) {
			canvas.setVisible(false);
			imgView.setVisible(true);
			imgView.setImage(backgroungImage);
		}
		progressBar.setProgress(frame / frameCount);
	}

	public void playStopButtonFired(ActionEvent event) {
		if (playStopButton.getText().toString().equals("Play")) {
			Continue = true;
			playStopButton.setText("Stop");
		} else {
			Continue = false;
			playStopButton.setText("Play");
		}
	}

	public void closeItemFired(ActionEvent event) {
		System.exit(0);
	}
	
	public void resetToDefaultButton(ActionEvent event) {
		dt_slider.setValue(0.2);
		noise_slider.setValue(0.5);
		distance_slider.setValue(100);
		skipedFrames_slider.setValue(15);
		traceLength_slider.setValue(10);
		minArea_slider.setValue(500);
		maxArea_slider.setValue(3000);
		learningRate_slider.setValue(-1);
		kernelSize_slider.setValue(4);
		
	}

	public void fileChooser() {
		FileChooser fc = new FileChooser();
		File f = fc.showOpenDialog(null);
		if (f != null) {
			uri = f.getAbsolutePath();
		}
		gc = canvas.getGraphicsContext2D();
		firstImage = getFirstImage();
		gc.drawImage(firstImage, 0, 0);
		uriDone = true;
	}

	public Image getFirstImage() {
		Mat matFirstImage = new Mat();
		Image firstImage;
		VideoCapture camera = new VideoCapture(uri);
		Mat frame = new Mat();
		if (camera.read(frame)) {
			Size sz = new Size(640, 480);
			Imgproc.resize(frame, frame, sz);
			matFirstImage = frame.clone();
		}
		firstImage = Detect.Mat2Image(matFirstImage);
		return firstImage;

	}

	@Override
	public void initialize(URL arg0, ResourceBundle arg1) {
		// TODO Auto-generated method stub
		GraphicsContext gc = canvas.getGraphicsContext2D();
		gc.setFill(Color.BLACK);
		gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
		
		groupModeLine.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {

            @Override
            public void changed(
                    ObservableValue<? extends Toggle> arg0,
                    Toggle wasPreviouslySelected, Toggle isNowSelected) {

                if(isNowSelected.equals(radioButtonEveryWhere) || isNowSelected.equals(radioButtonStaticLine) ){
                	reset(canvas);
                }
                if ((isNowSelected.equals(radioButtonLine) || isNowSelected.equals(radioButtonArea)) && !(app.getLines().size()==0)) {
					gc.setFill(Color.RED);
					pomocnePoints = app.getLines().get(0).getPoints();
				}

            }
        });

		canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent e) {
				if (radioButtonArea.isSelected() || radioButtonLine.isSelected()) {
					gc.setFill(Color.RED);
					gc.fillRect(e.getX() - 2, e.getY() - 2, pixelArea, pixelArea);
					addPoint(e);
					
				}
				
			}

		});

		canvas.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent e) {
				if (radioButtonArea.isSelected() || radioButtonLine.isSelected()) {
					if (radioButtonLine.isSelected()) {
						copyPointsToLine(pomocnePoints);
					}
					if (radioButtonArea.isSelected()) {
						return;
					}
				}	
			}

			private void copyPointsToLine(Vector<Point> points) {
				DetectionLine line = new DetectionLine();
				for (int i = 0; i < points.size(); i++) {
					line.addPoint(points.get(i));
				}
				app.addLine(line);

			}

		});

		canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent t) {
				if (radioButtonArea.isSelected() && t.getButton() == MouseButton.SECONDARY) {
					Point click = new Point(t.getX(), t.getY());
					lastPointToArea(click);

				}
				if (t.getClickCount() == 2) {
					reset(canvas);
				}
				return;
			}

			private void lastPointToArea(Point point) {
				DetectionArea area = new DetectionArea();
				HashSet<Point> hashSet = new HashSet<Point>();
				app.gc = gc;
				app.imageView = imgView;
				app.controller = controller;
				hashSet = app.vytvorOblast(point);
				area.addPoint(hashSet);
				app.addArea(area);

			}

		});
		
		progressBar.setOnMouseDragged(new EventHandler<MouseEvent>() {
			@Override
			public void handle(MouseEvent mouseEvent) {
				progressBar.getLayoutX();
				double newProgress = mouseEvent.getX();
				double zacatekProgressBaru = progressBar.getScaleX();
				double konecProgressBaru = progressBar.getWidth();
				double setProgress = (newProgress) / (konecProgressBaru - zacatekProgressBaru);
				detect.camera.set(Videoio.CAP_PROP_POS_FRAMES, setProgress * frameCount);
				progressBar.setProgress(setProgress);
			}
		});
		
		
		menuHelpControls.setOnAction(new EventHandler<ActionEvent>() {
		    public void handle(ActionEvent t) {
		    	TextArea controls = new TextArea();
		    	controls.setWrapText(true);
		    	controls.setEditable(false);
		        controls.appendText("-First of all you need to select video from you disk in which you want to detect moving objects by clicking File - Open." + "\n" + "\n"
		    	+ "-After you succesfully loaded video from you disk you shoud be able to see first image of that video in application screen." + "\n" + "\n"
		        + "-At this moment you can choose which mode of detection or image display you want. RGB and Background are simply two modes that shows either RGB image or Background substracted image."  + "\n" + "\n"
		    	+ "-If you select mode static line or detect everywhere you can simply hit the play button and application will proceed." + "\n" + "\n"
		        + "-If you select mode draw line before you hit the play button, you have to draw line into image displayed on your screen by holding left mouse button just like in Windows Paint, but unlike Windows Paint you are able to reset everything that was drawn by double clicking left mouse button."  + "\n" + "\n"
		    	+ "-If you select mode draw area before you hit the play button, you have to draw continuous line that beggin and ends in the same spot in other words you need to draw an area. After that you need to tell this application if you want to detect inside the area or outside. You can do that by click right mouse button either inside or outside your area. If everything was done correctly you will recieve a message telling you how big your area is. You can also reset everything that was drawn by double clicking left mouse button." + "\n" + "\n" 
		        + "-NOTE THAT YOU CAN ONLY CHANGE MODES WHEN THE VIDEO IS STOPPED");
		        Stage stage = new Stage();
		        Scene scene = new Scene(new VBox(controls));
		        stage.setTitle("popup");
		        stage.setScene(scene);
		        stage.show();
		        
		    }
		});
		
		menuHelpAdvancedSetting.setOnAction(new EventHandler<ActionEvent>() {
		    public void handle(ActionEvent t) {
		    	TextArea controls = new TextArea();
		    	controls.setWrapText(true);
		    	controls.setEditable(false);
		        controls.appendText(
		    	 "This should give you a general idea about what variables you can set yourself and what they do." + "\n" + "\n"
		        + "dT is delta time. Default value is 0.2."  + "\n" + "\n"
		    	+ "Noise is pretty self explanatory, you can set how much noise you are expecting at your inputs. Default value is 0.5." + "\n" + "\n"
		        + "Distance is how much can one track of kalman filter jump to another object when searching for its source. Default value is 100."  + "\n" + "\n"
		    	+ "Skipped frames is value of how many frames can one track of kalman filter be without its source. After number of set skipped frames pass track will be deleted. Default value is 15." + "\n" + "\n"
		    	+ "Trace Length is simply value of how many past values are displayed by one track of kalman filter in other words how long the lines of kalman filter are. Default value is 10."  + "\n" + "\n"
		    	+ "minArea is a value of how big can the smallest object be to still be detected by this application. Default value is 500."  + "\n" + "\n"
		    	+ "maxArea is a value of how big can the largest object be to still be detected by this application. Default value is 3000."  + "\n" + "\n"
		    	+ "Learning rate value indicates how fast the background model is learnt. If you set it to negative algorithm will choose the value automatically. If you set it to 0 the background model will not be updated at all and if you set it to 1 background model is completely reinitialized from the last frame. Defaul value is -1."  + "\n" + "\n"
		    	+ "Kernel Size specifies size of the matrix. Matrix is always n x n. Default value is 4."  + "\n" + "\n");
		        Stage stage = new Stage();
		        Scene scene = new Scene(new VBox(controls));
		        stage.setTitle("popup");
		        stage.setScene(scene);
		        stage.show();
		        
		    }
		});
	}

	private void addPoint(MouseEvent e) {
		for (int i = 0; i < pixelArea; i++) {
			for (int j = 0; j < pixelArea; j++) {

				pomocnePoints.add(new Point((int) e.getX() - 2 + i, (int) e.getY() - 2 + j));

			}
		}
	}

	private void reset(Canvas canvas) {
		//textArea.appendText("Canvas will be empty after you hit play button again" + "\n");
		GraphicsContext gc = canvas.getGraphicsContext2D();
		gc.setFill(Color.BLACK);
		gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
		gc.drawImage(currentImage, 0, 0);
		pomocnePoints.clear();
		if (uriDone) {
			update(currentImage, currenBackgroundImage);
		}
		
	}

}
