package org.hildan.fxlog.coloring;

import javafx.beans.binding.Binding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

import org.hildan.fxlog.core.LogEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * A binding that computes the first {@link StyleRule} of a {@link Colorizer} that matches a {@link LogEntry}. It stays
 * up-do-date with respect to the log and the colorizer's rules change events.
 */
public class FirstMatchingRuleBinding extends ObjectBinding<StyleRule> {

    private final RuleContentBinder ruleContentBinder = new RuleContentBinder();

    private final ObservableValue<Colorizer> colorizerObservableValue;

    private final ObservableValue<LogEntry> logEntryObservableValue;

    FirstMatchingRuleBinding(ObservableValue<Colorizer> colorizer, ObservableValue<LogEntry> logEntry) {
        colorizerObservableValue = colorizer;
        logEntryObservableValue = logEntry;

        Colorizer currentColorizer = colorizer.getValue();
        bindRulesList(currentColorizer);
        colorizer.addListener((obs, oldColorizer, newColorizer) -> {
            System.out.println("Colorizer changed to " + newColorizer.getName());
            unbindRulesList(oldColorizer);
            bindRulesList(newColorizer);
            invalidate();
        });

        bind(logEntry);
    }

    private void bindRulesList(Colorizer colorizer) {
        if (colorizer != null) {
            ObservableList<StyleRule> rules = colorizer.getRules();
            bind(rules);
            rules.forEach(this::bindRuleContent);
            rules.addListener(ruleContentBinder);
        }
    }

    private void unbindRulesList(Colorizer colorizer) {
        if (colorizer != null) {
            ObservableList<StyleRule> rules = colorizer.getRules();
            unbind(rules);
            rules.removeListener(ruleContentBinder);
            rules.forEach(this::unbindRuleContent);
        }
    }

    private void bindRuleContent(StyleRule styleRule) {
        System.out.println("Binding to rule " + styleRule.getName());
        Binding[] observables = styleRule.getMatchingInternalsObservable();

        // this is crazy, but required to get instant updates
        // for some reason, this binding does not get invalidated if these dependencies are not re-computed
        // TODO find a way to get invalidated without forcing re-computation like this
        for (Binding observable : observables) {
            observable.addListener(o -> observable.getValue());
        }

        bind(observables);
    }

    private void unbindRuleContent(StyleRule styleRule) {
        unbind(styleRule.getMatchingInternalsObservable());
    }

    @Override
    protected StyleRule computeValue() {
        return getMatchingRule(colorizerObservableValue.getValue(), logEntryObservableValue.getValue());
    }

    @NotNull
    private static StyleRule getMatchingRule(@Nullable Colorizer colorizer, @Nullable LogEntry log) {
        System.out.println("Recomputing matching rule for log: " + (log == null ? null : log.toString()));
        if (colorizer == null || log == null) {
            return StyleRule.DEFAULT;
        }
        return colorizer.getRules().stream().filter(r -> r.matches(log)).findFirst().orElse(StyleRule.DEFAULT);
    }

    private class RuleContentBinder implements ListChangeListener<StyleRule> {

        @Override
        public void onChanged(Change<? extends StyleRule> change) {
            while (change.next()) {
                if (change.wasAdded()) {
                    change.getAddedSubList().forEach(FirstMatchingRuleBinding.this::bindRuleContent);
                }
                if (change.wasRemoved()) {
                    change.getRemoved().forEach(FirstMatchingRuleBinding.this::unbindRuleContent);
                }
            }
        }
    }
}

@@ -29,16 +29,48 @@
 oring;%0A%0A
+import javafx.beans.Observable;%0A
 import j
@@ -90,32 +90,32 @@
 inding.Binding;%0A
-
 import javafx.be
@@ -1272,90 +1272,8 @@
 %3E %7B%0A
-            System.out.println(%22Colorizer changed to %22 + newColorizer.getName());%0A
     
@@ -2085,78 +2085,8 @@
 ) %7B%0A
-        System.out.println(%22Binding to rule %22 + styleRule.getName());%0A
     
@@ -2173,253 +2173,71 @@
  // 
-this is crazy, but required to get instant updates%0A        // for some reason, this binding does not get invalidated if these dependencies are not re-computed%0A        // TODO find a way to get invalidated without forcing re-computation like this
+TODO see issue https://github.com/joffrey-bion/fx-log/issues/67
 %0A   
@@ -2696,16 +2696,16 @@
 NotNull%0A
+
     priv
@@ -2802,115 +2802,8 @@
 ) %7B%0A
-        System.out.println(%22Recomputing matching rule for log: %22 + (log == null ? null : log.toString()));%0A
     

Add link to issue #67 next to the current rule matching binding workaround