package be.iminds.ilabt.jfed.experimenter_gui.debug;

import be.iminds.ilabt.jfed.util.ExecuteOnNotNull;
import be.iminds.ilabt.jfed.ui.javafx.util.StageUtils;
import be.iminds.ilabt.jfed.logging.JFedLoggerManager;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import ch.qos.logback.classic.Level;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.controlsfx.glyphfont.FontAwesome;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * User: twalcari
 * Date: 7/1/2015
 * Time: 14:27
 */

@Singleton
public class LogViewer {
    private static final Logger LOG = LoggerFactory.getLogger(LogViewer.class);
    private final VBox layout;
    private final LogView logView;
    private final ChoiceBox<Level> filterLevel;

    private final CheckBox showTimestamp;
    private final CheckBox showThreadname;
    private final CheckBox showLevel;
    private final TextField filterTextField;
    private final ToggleButton tail;
    private final ToggleButton pause;
    //    private final Slider rate;
//    private final Label rateLabel;
//    private final VBox rateLayout;
    private final HBox controls;
    private Stage stage;

    @Inject
    public LogViewer(JFedLoggerManager jFedLoggerManager) {

        LogViewAppender logViewAppender =
                new LogViewAppender(jFedLoggerManager.getBugReportMemoryAppender().getListCopy());
        registerLogViewAppender(logViewAppender);

        logView = new LogView(logViewAppender);
        logView.setPrefWidth(400);


        showTimestamp = new CheckBox("Show Timestamp");
        logView.showTimeStampProperty().bind(showTimestamp.selectedProperty());

        showThreadname = new CheckBox("Show Threadname");
        logView.showThreadnameProperty().bind(showThreadname.selectedProperty());

        showLevel = new CheckBox("Show Level");
        logView.showLevelProperty().bind(showLevel.selectedProperty());

        Pane filler = new Pane();
        HBox.setHgrow(filler, Priority.ALWAYS);


        tail = new ToggleButton("Tail");
        tail.setGraphic(GlyphUtils.createSmallGlyph(FontAwesome.Glyph.ANGLE_DOUBLE_DOWN));
        logView.tailProperty().bind(tail.selectedProperty());

        pause = new ToggleButton("Pause");
        pause.setGraphic(GlyphUtils.createSmallGlyph(FontAwesome.Glyph.PAUSE, Color.ORANGE));
        logView.pausedProperty().bind(pause.selectedProperty());

//        rate = new Slider(0.1, 60, 60);
//        logView.refreshRateProperty().bind(rate.valueProperty());
//        rateLabel = new Label();
//        rateLabel.textProperty().bind(Bindings.format("Update: %.2f fps", rate.valueProperty()));
//        rateLabel.setStyle("-fx-font-family: monospace;");
//        rateLayout = new VBox(rate, rateLabel);
//        rateLayout.setAlignment(Pos.CENTER);

        controls = new HBox(
                10,
                showTimestamp,
                showThreadname,
                showLevel,

                filler,
                tail,
                pause
                //, rateLayout
        );
        controls.setMinHeight(HBox.USE_PREF_SIZE);

        filterLevel = new ChoiceBox<>();

        filterLevel.getItems().addAll(Level.ERROR, Level.WARN, Level.INFO, Level.DEBUG, Level.TRACE, Level.ALL);

        filterLevel.getSelectionModel().select(Level.DEBUG);
        logView.filterLevelProperty().bind(
                filterLevel.getSelectionModel().selectedItemProperty()
        );

        filterTextField = new TextField();
        logView.textFilterProperty().bind(filterTextField.textProperty());

        HBox filters = new HBox(10,
                new Label("Level:"), filterLevel,
                new Label("Filter: "), filterTextField);


        layout = new VBox(
                10,
                controls,
                logView,
                filters
        );
        VBox.setVgrow(logView, Priority.ALWAYS);

        new ExecuteOnNotNull<Scene>(logView.sceneProperty()) {
            @Override
            public void run() {
                logView.prefWidthProperty().bind(logView.getScene().widthProperty().subtract(5));
            }
        };
    }

    public void show() {
        if (stage == null) {
            LOG.debug("Creating a new stage for LogViewer");
            Scene scene = new Scene(layout);
            scene.getStylesheets().add(LogViewer.class.getResource("log-view.css").toExternalForm());
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.L, KeyCodeCombination.CONTROL_DOWN), filterLevel::requestFocus);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.F, KeyCodeCombination.CONTROL_DOWN), filterTextField::requestFocus);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.P, KeyCodeCombination.CONTROL_DOWN), pause::fire);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.T, KeyCodeCombination.CONTROL_DOWN), tail::fire);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.F5), showTimestamp::fire);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.F6), showThreadname::fire);
            scene.getAccelerators().put(new KeyCodeCombination(KeyCode.F7), showLevel::fire);

            stage = new Stage();
            stage.setTitle("jFed Log Viewer");
            StageUtils.setJFedStageIcons(stage);

            stage.setWidth(800);
            stage.setHeight(600);
            stage.setScene(scene);
        }
        LOG.debug("Showing the new stage");
        stage.show();
    }

    private static void registerLogViewAppender(LogViewAppender logViewAppender) {
        org.slf4j.Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

        if (!(rootLogger instanceof ch.qos.logback.classic.Logger)) {
            LOG.error("rootLogger is not instance of ch.qos.logback.classic.Logger but of " + rootLogger.getClass().getName());
            System.out.println("NOTE: logging cannot be modified by jFed: rootLogger is not instance of ch.qos.logback.classic.Logger but of " + rootLogger.getClass().getName());
            return;
        }
        final ch.qos.logback.classic.Logger logbackRootLogger = (ch.qos.logback.classic.Logger) rootLogger;

        logViewAppender.setContext(logbackRootLogger.getLoggerContext());
        logbackRootLogger.addAppender(logViewAppender);

        logViewAppender.start();

        LOG.debug("Registered logViewAppender on the rootLogger");

    }
}
