import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import java.time.temporal.ChronoUnit;

/**
 * Contiene la vista y funcionalidad de un temporizador que implementa la técnica Pomodoro para
 * que el usuario mejore la administración del tiempo dedicado a una actividad
 */
public class PomodoroTimer extends Widget {

    final private Integer MIN = 60;
    private TimerState state = TimerState.WORK;
    private ConfigStatus cState = ConfigStatus.CLOSED;
    private Integer MAXWORK = 4;
    private Integer WORKTIME = 25;  //Minutos de Trabajo
    private Integer RESTTIME = 5; //Minutos de Descanso
    private Integer LONGRESTTIME = 15; //Minutos de Descanso Largo (cada 4)
    private int count = 0;
    private Group view;

    private static final String bellShort = AudioMixer.class.getResource("alarms/BellsShort.mp3").toExternalForm();
    private static final String chime = AudioMixer.class.getResource("alarms/Chime.mp3").toExternalForm();
    private static final String chimes = AudioMixer.class.getResource("alarms/Chimes.mp3").toExternalForm();
    private static final String cymbals = AudioMixer.class.getResource("alarms/FingerCymbals.mp3").toExternalForm();
    private static final String bellRing = AudioMixer.class.getResource("alarms/GameShowBellRing.mp3").toExternalForm();
    private static final String littleBell = AudioMixer.class.getResource("alarms/LittleBell.mp3").toExternalForm();
    private static final String success = AudioMixer.class.getResource("alarms/Success.mp3").toExternalForm();

    public ObjectProperty<java.time.Duration> remainingDuration;

    /**
     * Constructor de PomodoroTimer
     * @param icono Ubicación de la imagen que actúa como ícono para abrir y cerrar el PomodoroTimer
     */
    public PomodoroTimer(String icono){
        super(icono);
        view = new Group();
        super.createAnimation(view);

        // Create label to display the time left:
        Label countDownLabel = new Label();

        // Create duration property for time remaining:
        remainingDuration = new SimpleObjectProperty<>(java.time.Duration.ofSeconds((long) WORKTIME *MIN));



        //Ajustes Visuales Label
        countDownLabel.setTextFill(Color.WHITE);
        countDownLabel.setStyle("-fx-font-family: Impact ; -fx-font-size: 60");

        // Binding with media time format (hh:mm:ss):
        countDownLabel.textProperty().bind(Bindings.createStringBinding(() ->
                        String.format("%02d:%02d:%02d",
                                remainingDuration.get().toHours(),
                                remainingDuration.get().toMinutes() % 60,
                                remainingDuration.get().toSeconds() % 60),
                remainingDuration));

        // Create time line to lower remaining duration every second:
        Timeline countDownTimeLine = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent event) ->
                remainingDuration.setValue(remainingDuration.get().minus(1, ChronoUnit.SECONDS))));

        // Set number of cycles (remaining duration in seconds):
        countDownTimeLine.setCycleCount((int) remainingDuration.get().getSeconds());

        //Cambio de temporizador

        countDownTimeLine.setOnFinished(event -> {
            audioPlayer = new MediaPlayer(new Media(audio));
            audioPlayer.seek(audioPlayer.getStartTime());
            audioPlayer.play();
            state = state==TimerState.WORK ? TimerState.REST : TimerState.WORK;
            System.out.println(""+ state);
            if (state == TimerState.REST) {
                if (count == MAXWORK){
                    count = 0;
                    System.out.println(" "+ state);
                    remainingDuration.setValue(java.time.Duration.ofSeconds((long) LONGRESTTIME * MIN));
                } else
                remainingDuration.setValue(java.time.Duration.ofSeconds((long) RESTTIME * MIN));
            } else if (state == TimerState.WORK) {
                count++;
                remainingDuration.setValue(java.time.Duration.ofSeconds((long) WORKTIME * MIN));
            }
            countDownTimeLine.setCycleCount((int) remainingDuration.get().getSeconds());
            countDownTimeLine.play();
        });

        Image playIcon = new Image("icons/play.png");
        Image configIcon = new Image("icons/bell.png");
        Image pauseIcon = new Image("icons/pause.png");
        Image skipIcon = new Image("icons/forward.png");
        Image againIcon = new Image("icons/refresh.png");
        Image plusIcon = new Image("icons/add.png");
        Image closeIcon = new Image("icons/close.png");

        ImageView play = new ImageView(playIcon);
        ImageView pause = new ImageView(pauseIcon);
        ImageView skip = new ImageView(skipIcon);
        ImageView config = new ImageView(configIcon);
        ImageView again = new ImageView(againIcon);
        ImageView plus = new ImageView(plusIcon);
        ImageView close = new ImageView(closeIcon);

        play.setFitHeight(15);
        play.setFitWidth(15);

        pause.setFitHeight(15);
        pause.setFitWidth(15);

        skip.setFitHeight(15);
        skip.setFitWidth(15);

        config.setFitHeight(15);
        config.setFitWidth(15);

        again.setFitHeight(15);
        again.setFitWidth(15);

        plus.setFitHeight(15);
        plus.setFitWidth(15);

        close.setFitHeight(10);
        close.setFitWidth(10);


        Button bPause = new Button();
        Button bPlay = new Button();
        Button bConfig = new Button();
        Button bAgain = new Button();
        Button bPlus = new Button();
        Button bSkip = new Button();

        bPause.getStyleClass().add("buttonT");
        bPlay.getStyleClass().add("buttonT");
        bPlus.getStyleClass().add("buttonT");
        bSkip.getStyleClass().add("buttonT");
        bAgain.getStyleClass().add("buttonT");
        bConfig.getStyleClass().add("buttonT");


        bPause.setGraphic(pause);
        bPlay.setGraphic(play);
        bSkip.setGraphic(skip);
        bConfig.setGraphic(config);
        bAgain.setGraphic(again);
        bPlus.setGraphic(plus);


        bPause.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                countDownTimeLine.pause();
            }
        });
        bPlay.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                countDownTimeLine.play();
            }
        });
        bAgain.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                countDownTimeLine.stop();
                state = TimerState.WORK;
                remainingDuration.setValue(java.time.Duration.ofSeconds((long) WORKTIME * MIN));
            }
        });
        bPlus.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                if (state == TimerState.REST){
                    RESTTIME++;
                } else if (state == TimerState.WORK){
                    WORKTIME++;
                }
                remainingDuration.setValue(remainingDuration.get().plus(1*MIN, ChronoUnit.SECONDS));
            }
        });
        bSkip.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                countDownTimeLine.stop();
                state = state==TimerState.WORK ? TimerState.REST : TimerState.WORK;
                System.out.println(""+ state);
                if (state == TimerState.REST) {
                    remainingDuration.setValue(java.time.Duration.ofSeconds((long) RESTTIME * MIN));
                } else if (state == TimerState.WORK) {
                    remainingDuration.setValue(java.time.Duration.ofSeconds((long) WORKTIME * MIN));
                }
                countDownTimeLine.setCycleCount((int) remainingDuration.get().getSeconds());
                countDownTimeLine.play();
            }
        });


        HBox buttonBox =new HBox(10);
        buttonBox.setAlignment(Pos.CENTER);
        buttonBox.getChildren().addAll(bPause,bPlay,bAgain,bPlus,bSkip,bConfig);


        VBox vb = new VBox(20);          // gap between components is 20
        vb.setAlignment(Pos.CENTER);        // center the components within VBox
        vb.getChildren().addAll(countDownLabel,buttonBox);
        vb.setLayoutY(30);

        Label  workTime = new Label("Duraci\u00f3n pomodoro:");
        Label  restTime = new Label("Duraci\u00f3n descanso corto:");
        Label  longRestTime = new Label("Duraci\u00f3n descanso largo:");
        Label  maxWork = new Label("Retraso descanso largo:");

        workTime.setStyle("-fx-font-family: Consolas");
        restTime.setStyle("-fx-font-family: Consolas");
        longRestTime.setStyle("-fx-font-family: Consolas");
        workTime.setStyle("-fx-font-family: Consolas");

        Spinner<Integer> workS = new Spinner<>(0, 60, 25);
        Spinner<Integer> restS = new Spinner<>(0,60,5);
        Spinner<Integer> longS = new Spinner<>(0,60,15);
        Spinner<Integer> maxS = new Spinner<>(0,10,4);

        workS.valueProperty().addListener(
            new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                    WORKTIME= newValue.intValue();
                   if(state == TimerState.WORK) {
                       countDownTimeLine.stop();
                       remainingDuration.setValue(java.time.Duration.ofSeconds((long) WORKTIME * MIN));
                       countDownTimeLine.setCycleCount((int) remainingDuration.get().getSeconds());
                       countDownTimeLine.play();
                   }
                }
            }
        );

        restS.valueProperty().addListener(
            new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                    RESTTIME= newValue.intValue();
                    if(state == TimerState.REST) {
                        countDownTimeLine.stop();
                        remainingDuration.setValue(java.time.Duration.ofSeconds((long) RESTTIME * MIN));
                        countDownTimeLine.setCycleCount((int) remainingDuration.get().getSeconds());
                        countDownTimeLine.play();
                    }

                }
            }
        );

        longS.valueProperty().addListener(
            new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                    LONGRESTTIME= newValue.intValue();
                }
            }
        );

        maxS.valueProperty().addListener(
                new ChangeListener<Number>() {
                    @Override
                    public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                        MAXWORK= newValue.intValue();
                    }
                }
        );

        VBox labels = new VBox(27,workTime,restTime,longRestTime,maxWork);
        VBox spinners = new VBox(20,workS,restS,longS,maxS);

        labels.setAlignment(Pos.CENTER_LEFT);
        spinners.setAlignment(Pos.CENTER_LEFT);

        Button closeConfig = new Button();
        closeConfig.getStyleClass().add("buttonX");
        closeConfig.setGraphic(close);

        String sounds[] =
                { "Bell", "Chime", "Chimes",
                        "Finger Cymbal", "Bell Ring" , "Little Bell", "Success" };

        String alarm[] =
                { bellShort, chime, chimes,
                        cymbals, bellRing , littleBell, success };



        ComboBox<String> comboBox =
                new ComboBox<>(FXCollections
                        .observableArrayList(sounds));

        // Create action event
        EventHandler<ActionEvent> event =
            new EventHandler<ActionEvent>() {
                public void handle(ActionEvent e)
                {
                    for (int i = 0; i < sounds.length ; i++) {
                        if(comboBox.getValue().equals(sounds[i])) audio = alarm[i];
                    }

                }
            };


        Slider alarmVolume = new Slider(0,100,1);
        alarmVolume.getStyleClass().add("sliderT");

        alarmVolume.valueProperty().addListener(
                new ChangeListener<Number>() {
                    public void changed(ObservableValue<? extends Number >
                                                observable, Number oldValue, Number newValue)
                    {
                        audioPlayer.setVolume(newValue.doubleValue());
                    }
                });

        // Set on action
        comboBox.setOnAction(event);
        comboBox.getSelectionModel().selectFirst();

        HBox configBox = new HBox(20);
        HBox configSound = new HBox(20, comboBox,alarmVolume);
        configSound.setAlignment(Pos.CENTER);
        configSound.setPadding(new Insets(10));

        configBox.setPadding(new Insets(20));
        configBox.getChildren().addAll(labels,spinners);


        VBox settings = new VBox(2,  closeConfig, configBox, configSound);
        settings.setBackground(new Background(new BackgroundFill(Color.web("#E4E1FF"), new CornerRadii(10), new Insets(-10))));
        settings.setAlignment(Pos.CENTER_RIGHT);

        view.getChildren().add(vb);


        bConfig.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                cState = ConfigStatus.OPEN;
                view.getChildren().remove(vb);
                view.getChildren().add(settings);
            }
        });

        closeConfig.setOnAction(new EventHandler<ActionEvent>() {
            public void handle(ActionEvent event) {
                cState = ConfigStatus.CLOSED;
                view.getChildren().remove(settings);
                view.getChildren().add(vb);
            }
        });

        vb.getStyleClass().add("timer");

    }

    private String audio = bellShort;
    MediaPlayer audioPlayer = new MediaPlayer(new Media(audio));
}
