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

import be.iminds.ilabt.jfed.bugreport.LogLine;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
import javafx.css.PseudoClass;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.text.Text;

import java.text.SimpleDateFormat;
import java.util.Objects;

/**
 * User: twalcari
 * Date: 7/1/2015
 * Time: 10:58
 */
public class LogView extends ListView<LogLine> {
    private static final int MAX_ENTRIES = 10_000;

    private final static PseudoClass trace = PseudoClass.getPseudoClass("trace");
    private final static PseudoClass debug = PseudoClass.getPseudoClass("debug");
    private final static PseudoClass info = PseudoClass.getPseudoClass("info");
    private final static PseudoClass warn = PseudoClass.getPseudoClass("warn");
    private final static PseudoClass error = PseudoClass.getPseudoClass("error");

    private final static SimpleDateFormat timestampFormatter = new SimpleDateFormat("HH:mm:ss.SSS");

    private final BooleanProperty showTimestamp = new SimpleBooleanProperty(true);
    private final BooleanProperty showLevel = new SimpleBooleanProperty(false);
    private final BooleanProperty showThreadname = new SimpleBooleanProperty(false);


    public BooleanProperty showTimeStampProperty() {
        return showTimestamp;
    }

    public boolean getShowLevel() {
        return showLevel.get();
    }

    public BooleanProperty showLevelProperty() {
        return showLevel;
    }

    public boolean getShowThreadname() {
        return showThreadname.get();
    }

    public BooleanProperty showThreadnameProperty() {
        return showThreadname;
    }

    public boolean getShowTimestamp() {
        return showTimestamp.get();
    }

    public BooleanProperty showTimestampProperty() {
        return showTimestamp;
    }

    public LogView() {
        getStylesheets().add(getClass().getResource("log-view.css").toExternalForm());
        getStyleClass().add("log-view");

        setCellFactory(param -> new ListCell<LogLine>() {

            final Text text = new Text();

            {
                showTimestamp.addListener(observable -> updateItem(this.getItem(), this.isEmpty()));

                text.getStyleClass().add("log-event");
                text.wrappingWidthProperty().bind(widthProperty());

                setGraphic(text);

                prefWidthProperty().bind(LogView.this.widthProperty().subtract(3));
            }

            @Override
            protected void updateItem(LogLine item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    text.setText(null);
                    return;
                }

                StringBuilder sb = new StringBuilder();

                if (showTimestamp.get()) {
                    sb.append(timestampFormatter.format(item.getTime())).append(" ");
                }

                if (showThreadname.get()) {
                    sb.append("[").append(item.getThreadName()).append("] ");
                }

                if (showLevel.get()) {
                    sb.append(item.getLevel()).append("\t ");
                }

                if (item.getClassName() != null) {
                    sb.append(item.getClassName()).append(" ");
                }

                sb.append(item.getMessage());

                if (item.getMessage() != null) {
                    sb.append("\n").append(item.getMessage());
                }

                text.setText(sb.toString());


                text.pseudoClassStateChanged(trace, Objects.equals(item.getLevel(), "TRACE"));
                text.pseudoClassStateChanged(debug, Objects.equals(item.getLevel(), "DEBUG"));
                text.pseudoClassStateChanged(info, Objects.equals(item.getLevel(), "INFO"));
                text.pseudoClassStateChanged(warn, Objects.equals(item.getLevel(), "WARN"));
                text.pseudoClassStateChanged(error, Objects.equals(item.getLevel(), "ERROR"));
            }
        });

        showTimestamp.addListener(observable -> refreshView());
        showThreadname.addListener(observable -> refreshView());
        showLevel.addListener(observable -> refreshView());
    }

    private void refreshView() {
        ObservableList<LogLine> logItems = getItems();

        setItems(null);
        setItems(logItems);
    }

    public enum Level {
        TRACE,
        DEBUG,
        INFO,
        WARN,
        ERROR
    }

}
