package ru.kirillius.cooler.controller.UI;

import ru.kirillius.cooler.controller.API.DataType;
import ru.kirillius.cooler.controller.API.MessageSystem;
import ru.kirillius.cooler.controller.API.Operation;
import ru.kirillius.cooler.controller.ExtMath;

import javax.swing.*;

public abstract class ReactiveComponentController<C extends JComponent> {
    protected DataType dataType;
    protected MessageSystem messageSystem;
    protected C component;
    protected String units;
    protected float minValue;
    protected float maxValue;
    protected float multiplier;
    protected boolean lock = true;
    protected JLabel label;
    private volatile float lastUpdateValue = Float.NEGATIVE_INFINITY;
    private volatile Operation updateOperation = null;

    public ReactiveComponentController(DataType dataType, MessageSystem messageSystem, C component, JLabel label, String units, float minValue, float maxValue, float multiplier) {
        this.dataType = dataType;
        this.multiplier = multiplier;
        this.label = label;
        this.messageSystem = messageSystem;
        this.component = component;
        this.units = units;
        this.minValue = minValue;
        this.maxValue = maxValue;

        label.setText(units);
    }

    public void reloadValue() {
        Operation operation = messageSystem.getValue(dataType);
        operation.waitForReturn();
        if (operation.isSuccessful()) setValue((int) operation.getValue() & 0xFF);
    }

    public void setLock(boolean lock) {
        this.lock = lock;
        component.setEnabled(!lock);
    }

    protected void updateSystem(float value) {
        if (messageSystem == null) return;
        if (!lock) {
            value = ExtMath.lerp(minValue, maxValue, ExtMath.clamp(value, 100, 0) / 100);
            updateLabel(value);
            startUpdateOperation(value);
        }
    }

    private synchronized void startUpdateOperation(float value) {
        if (updateOperation != null && !updateOperation.isComplete()) {
            lastUpdateValue = value;
            return;
        }
        lastUpdateValue = value;
        updateOperation = messageSystem.setValue(dataType, (byte) (0xFF & (int) value));
        updateOperation.addCompleteListener(eventData -> {
            if (value != lastUpdateValue) startUpdateOperation(lastUpdateValue);
        });
    }

    public void setValue(float value) {
        var t = ExtMath.inverseLerp(minValue, maxValue, value);
        SwingUtilities.invokeLater(() -> {
            changeComponentValue(ExtMath.clamp(t * 100f, 100, 0));
            updateLabel(value);
        });
    }

    protected void updateLabel(float value) {
        label.setText((int) (value / multiplier) + " " + units);
    }

    protected abstract void changeComponentValue(float value);

    public abstract void destroy();


}
