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

import be.iminds.ilabt.jfed.groovy_dsl.DSLStreamException;
import be.iminds.ilabt.jfed.gts.GtsDslReader;
import be.iminds.ilabt.jfed.gts.GtsDslWriter;
import be.iminds.ilabt.jfed.gts.model.impl.FXGtsType;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.StringReader;
import java.util.Objects;

/**
 * User: twalcari
 * Date: 5/21/2015
 * Time: 11:22
 */
public class EditableGtsDsl {
    private static final Logger LOG = LoggerFactory.getLogger(EditableGtsDsl.class);

    private final ReadOnlyObjectWrapper<FXGtsType> model = new ReadOnlyObjectWrapper<>();

    private final ReadOnlyStringWrapper name = new ReadOnlyStringWrapper();
    private final ReadOnlyStringWrapper dsl = new ReadOnlyStringWrapper();

    @Nullable
    private String lastSavedName = null;
    @Nullable
    private String lastSavedDsl = null;

    public EditableGtsDsl(String name){
        this(new FXGtsType(name));
    }

    public EditableGtsDsl(FXGtsType model) {
        setModel(model);

    }

    /**
     * Creates an empty editable rspec
     */
    public EditableGtsDsl(String name, String dsl) {
        setDsl(name, dsl);
    }

    public String getName() {
        return name.get();
    }


    public void setDsl(String name, String dsl) {
        this.dsl.set(dsl);

        this.name.unbind();
        this.name.set(name);

        this.model.set(null);
    }

    public void setModel(FXGtsType model) {
        this.model.set(model);
        this.name.bindBidirectional(model.nameProperty());

        this.dsl.set(null);

    }

    public void setName(String name) {
        name = name.replace("-", "_");

        this.name.set(name);
    }

    public boolean verifyName(String name) {
        return name.matches("[A-Za-z0-9_]+");
    }

    public ReadOnlyStringProperty nameProperty() {
        return name.getReadOnlyProperty();
    }

    public FXGtsType getModel() {
        return model.get();
    }


    private String getCurrentDsl() {
        if (dsl.get() != null)
            return dsl.get();
        else {
            assert model.get() != null;

            GtsDslWriter writer = new GtsDslWriter(model.get());

            try {
                return writer.call();
            } catch (DSLStreamException e) {
                throw new RuntimeException("Error while saving GTS DSL model", e);
            }
        }
    }

    public String getDsl() {
        return dsl.get();
    }

    public boolean isDirty() {
        if (lastSavedDsl == null)
            return false;

        return !Objects.equals(getCurrentDsl(), lastSavedDsl);
    }

    public boolean revert() {
        if (lastSavedDsl == null)
            return false;

        assert lastSavedName != null;

        setDsl(lastSavedName, lastSavedDsl);
        return true;
    }

    public boolean tryConvertToModel() {
        if (model.get() != null)
            return true;


        assert dsl.get() != null;
        GtsDslReader reader = new GtsDslReader(new StringReader(dsl.get()));
        try {
            setModel((FXGtsType) reader.call());
            return true;
        } catch (GtsDslReader.GtsDslParseException ex) {
            LOG.error("Could not convert GTS DSL to model: {}", ex.getMessage(), ex);
            return false;
        }
    }

    public boolean convertToDsl() {
        if (model.get() == null)
            return true;

        GtsDslWriter writer = new GtsDslWriter(model.get());

        try {
            setDsl(model.get().getName(), writer.call());
            return true;
        } catch (DSLStreamException e) {
            throw new RuntimeException("Error while saving GTS DSL model", e);
        }
    }

    public void save() throws DSLStreamException {
        if (model.get() != null) {
            GtsDslWriter writer = new GtsDslWriter(model.get());
            save(model.get().getName(), writer.call());
        } else {
            save(name.get(),dsl.get());
        }
    }

    protected void save(String name, String dsl){
        this.lastSavedName = name;
        this.lastSavedDsl = dsl;
    }

    public boolean isEmpty() {
        return model.get() == null && dsl.get() == null;
    }

     /*
    private boolean isPortInUse(GtsPort port) {
        //noinspection SuspiciousMethodCalls
        assert model.get().getPorts().values().contains(port) :
                "only ports on the root of the GTSType are supported in this method!";


        for (GtsType.Adjacency adjacency : model.get().getAdjacencies()) {
            if (isPortInAdjacency(adjacency.getPortId1(), port) || isPortInAdjacency(adjacency.getPortId2(), port))
                return true;
        }
        return false;
    }

    private static boolean isPortInAdjacency(GtsType.PortId portId, GtsPort port) {
        return portId.getParentId() == null && portId.getPortId().equals(port.getId());
    }   */

}
