/* 
 * Copyright 2015 Adobe.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.adobe.ags.curly.controller;

import com.adobe.ags.curly.ApplicationState;
import static com.adobe.ags.curly.Messages.*;
import com.adobe.ags.curly.model.BatchRunnerResult;
import com.adobe.ags.curly.model.RunnerResult;
import com.adobe.ags.curly.model.TaskRunner;
import com.sun.javafx.collections.ObservableListWrapper;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.input.Clipboard;
import javafx.scene.input.DataFormat;
import javafx.scene.web.WebView;
import javafx.stage.FileChooser;
import javafx.util.StringConverter;

public class RunnerActivityController {

    public static final int REPORT_GENERATION_INTERVAL = 5;

    public static enum ReportStyle {
        BRIEF(0), DETAILED(1), FULL(2);
        public int level;

        ReportStyle(int lvl) {
            level = lvl;
        }
    }

    @FXML // fx:id="progressBar"
    private ProgressBar progressBar; // Value injected by FXMLLoader

    @FXML // fx:id="statusLabel"
    private Label statusLabel; // Value injected by FXMLLoader

    @FXML // fx:id="ellapsedLabel"
    private Label ellapsedLabel; // Value injected by FXMLLoader

    @FXML // fx:id="remainingLabel"
    private Label remainingLabel; // Value injected by FXMLLoader

    @FXML // fx:id="reportStyle"
    private ChoiceBox<ReportStyle> reportStyle; // Value injected by FXMLLoader

    @FXML // fx:id="goStopButton"
    private Button goStopButton; // Value injected by FXMLLoader

    @FXML // fx:id="reportWebview"
    private WebView reportWebview; // Value injected by FXMLLoader

    Thread runnerThread = null;

    @FXML
    void goStopClicked(ActionEvent event) {
        if (runnerThread == null) {
            runnerThread = new Thread(currentTask::run);
            runnerThread.start();
            hookupReportGenerator();
            statusLabel.textProperty().bind(Bindings
                    .when(currentTask.getResult().completed())
                    .then(
                            Bindings.when(currentTask.getResult().completelySuccessful())
                            .then(ApplicationState.getMessage(COMPLETED_SUCCESSFUL))
                            .otherwise(ApplicationState.getMessage(COMPLETED_UNSUCCESSFUL))
                    )
                    .otherwise(ApplicationState.getMessage(INCOMPLETE)));
            goStopButton.setText(ApplicationState.getMessage(STOP));
            goStopButton.visibleProperty().bind(Bindings.not(currentTask.getResult().completed()));
        } else {
            ApplicationState.getInstance().runningProperty().set(false);
            goStopButton.disableProperty();
            if (runnerThread != null && runnerThread.isAlive()) {
                runnerThread.interrupt();
                try {
                    runnerThread.join();
                } catch (InterruptedException ex) {
                    Logger.getLogger(RunnerActivityController.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            disableReportGenerator();
        }
    }

    @FXML
    void copyClicked(ActionEvent event) {
        Map<DataFormat, Object> reportFormats = new HashMap<>();
        reportFormats.put(DataFormat.HTML, getReportHtml());
        Clipboard.getSystemClipboard().setContent(reportFormats);
    }

    @FXML
    void saveClicked(ActionEvent event) {
        FileChooser saveChooser = new FileChooser();
        File saveFile = saveChooser.showSaveDialog(null);
        if (saveFile != null) {
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(saveFile))) {
                writer.write(getReportHtml());
                writer.close();
            } catch (IOException ex) {
                Logger.getLogger(RunnerActivityController.class.getName()).log(Level.SEVERE, null, ex);
            }

        }
    }

    @FXML // This method is called by the FXMLLoader when initialization is complete
    void initialize() {
        assert progressBar != null : "fx:id=\"progressBar\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert statusLabel != null : "fx:id=\"statusLabel\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert ellapsedLabel != null : "fx:id=\"ellapsedLabel\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert remainingLabel != null : "fx:id=\"remainingLabel\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert reportStyle != null : "fx:id=\"reportStyle\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert goStopButton != null : "fx:id=\"goStopButton\" was not injected: check your FXML file 'RunnerReport.fxml'.";
        assert reportWebview != null : "fx:id=\"reportWebview\" was not injected: check your FXML file 'RunnerReport.fxml'.";

        reportStyle.setItems(new ObservableListWrapper<>(Arrays.asList(ReportStyle.values())));
        reportStyle.setConverter(new StringConverter<RunnerActivityController.ReportStyle>() {
            @Override
            public String toString(ReportStyle style) {
                return ApplicationState.getMessage(style.toString().toLowerCase());
            }

            @Override
            public ReportStyle fromString(String string) {
                return ReportStyle.valueOf(string);
            }
        });
        Platform.runLater(() -> reportStyle.getSelectionModel().selectFirst());
        reportStyle.selectionModelProperty().addListener((prop, oldVal, newVal) -> generateReport());
    }

    TaskRunner currentTask;
    RunnerResult results;

    public void attachRunner(TaskRunner runner) {
        currentTask = runner;
        results = runner.getResult();
        if (results instanceof BatchRunnerResult) {
            BatchRunnerResult batchResults = (BatchRunnerResult) results;
            batchResults.timeEllapsedProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(()->ellapsedLabel.setText(timeString(newValue.longValue()))));
            batchResults.timeRemainingProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(()->remainingLabel.setText(timeString(newValue.longValue()))));
        } else {
            ellapsedLabel.setText("???");
            remainingLabel.setText("???");
        }
        progressBar.progressProperty().bind(results.percentComplete());
    }

    private String timeString(Long interval) {
        final long hr = TimeUnit.MILLISECONDS.toHours(interval);
        final long min = TimeUnit.MILLISECONDS.toMinutes(interval - TimeUnit.HOURS.toMillis(hr));
        final long sec = TimeUnit.MILLISECONDS.toSeconds(interval - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min));
        return String.format("%02d:%02d:%02d", hr, min, sec);
    }

    ScheduledExecutorService reportGenerator;
    ScheduledFuture reportTask;

    private void hookupReportGenerator() {
        reportGenerator = new ScheduledThreadPoolExecutor(1);
        reportTask = reportGenerator.scheduleWithFixedDelay(this::generateReport, 1, REPORT_GENERATION_INTERVAL, TimeUnit.SECONDS);
    }

    private void disableReportGenerator() {
        if (reportGenerator != null) {
            reportTask.cancel(false);
            reportGenerator.shutdown();
            reportGenerator = null;
        }
    }

    private String getReportHtml() {
        return results.toHtml(reportStyle.getSelectionModel().getSelectedItem().level);
    }

    private void generateReport() {
        try {
            if (results != null) {
                String html = getReportHtml();
                Platform.runLater(() -> reportWebview.getEngine().loadContent(html));
            }
        } catch (Throwable t) {
            Logger.getLogger(RunnerActivityController.class.getName()).log(Level.SEVERE, null, t);
        }
    }
}