package leigh.elements.javafx.type1;

import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import leigh.elements.ElementBase;
import leigh.elements.sdk.AppScreenInstance;
import leigh.elements.sdk.actions.ScreenAction;
import leigh.elements.sdk.type1.TextFieldElement;
import leigh.eo.EO;
import leigh.eo.EOListener;
import leigh.eo.EOLoop;
import leigh.eo.EditEventGenerator;
import leigh.mecha.lang.FutureResult;
import leigh.mecha.lang.FutureResultListener;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.Snavig;

import java.io.Serializable;

/**
 * Implementation of the Field element for JavaFX. Note that this implementation only provides the input field
 * itself, not a label. It is meant to be used with another element which will retrieve its label and other
 * information and decorate the field appropriately, for example, by including a label.
 * <p>
 * This class will update the <code>FieldElement.KEY_VALUE</code> attribute in the configuration whenever
 * the end-user changes the value in the field.
 *
 * @author Alex Leigh
 */
public class FieldElementJFX extends ElementBase implements ElementJFX, EOListener {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(FieldElementJFX.class);
    private HBox hbox = new HBox();
    private TextField input = new TextField();
    // Whether we should ignore the next change event. We do this to avoid a loop.

    protected FieldElementJFX(EO cfg, AppScreenInstance screen) {
        super(cfg);

        ((EditEventGenerator) cfg.getEditTracker()).addListener(cfg, this);

        String value = cfg.getValueString(TextFieldElement.KEY_VALUE);
        if (value != null) input.setText(value);
        input.textProperty().addListener((observable, oldValue, newValue) -> {
            logger.debug("Field value is changing. [oldValue: {}] [newValue: {}]", oldValue, newValue);
            if (!newValue.equals(cfg.getValueString(TextFieldElement.KEY_VALUE))) {
                cfg.setValue(TextFieldElement.KEY_VALUE, newValue);
            }
        });

        hbox.getChildren().add(input);

        EOLoop actions = cfg.getValueLoop(TextFieldElement.KEY_ACTIONS);
        for (EO action : actions) {
            String cmdFunc = action.getValueString(TextFieldElement.KEY_ACTION_FUNC);
            String cmdTarget = action.getValueString(TextFieldElement.KEY_ACTION_FUNC_TARGET);
            Button funcBtn = new Button("(f)");
            funcBtn.setOnAction(event -> {
                FutureResult<ScreenAction> future = screen.call(cmdTarget, cmdFunc);
                future.addListener(new FutureResultListener() {
                    @Override
                    public void receiveResult(Object result) {
                        screen.processAction((ScreenAction) result);
                    }
                });
            });
            hbox.getChildren().add(funcBtn);
        }
    }

    @Override
    public Node getJavaFXNode() {
        return hbox;
    }

    @Override
    public void valueUpdated(EO eo, String key, Serializable oldValue, Serializable newValue) {
        logger.debug("Inbound value event. [newValue: {}]", newValue);
        if (TextFieldElement.KEY_VALUE.equals(key) && !input.getText().equals(newValue)) {
            input.setText(Snavig.convertToString(newValue));
        }
    }
}
