package skadistats.clarity.analyzer.replay;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableListBase;
import skadistats.clarity.model.Entity;
import skadistats.clarity.model.FieldPath;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class ObservableEntity extends ObservableListBase<ObservableEntityProperty> {

    private final Entity entity;
    private final StringProperty index;
    private final StringProperty name;

    private List<FieldPath<? super FieldPath>> indices = new ArrayList<>();
    private List<ObservableEntityProperty> properties = new ArrayList<>();
    private boolean changeActive = false;

    public ObservableEntity(Entity entity) {
        this.entity = entity;
        index = new ReadOnlyStringWrapper(String.valueOf(entity.getIndex()));
        name = new ReadOnlyStringWrapper(entity.getDtClass().getDtName());
        Collection<FieldPath> fieldPaths = entity.getState().collectFieldPaths();
        for (FieldPath fieldPath : fieldPaths) {
            indices.add(fieldPath);
            properties.add(new ObservableEntityProperty(entity, fieldPath));
        }
    }

    public ObservableEntity(int index) {
        this.entity = null;
        this.index = new ReadOnlyStringWrapper(String.valueOf(index));
        this.name = new ReadOnlyStringWrapper("");
    }


    @Override
    public ObservableEntityProperty get(int index) {
        return properties.get(index);
    }

    @Override
    public int size() {
        return indices.size();
    }

    public <F extends FieldPath> int getIndexForFieldPath(F fieldPath) {
        return Collections.binarySearch(indices, fieldPath);
    }

    public ObservableEntityProperty getPropertyForFieldPath(FieldPath fieldPath) {
        return properties.get(getIndexForFieldPath(fieldPath));
    }

    public <T> ObjectBinding<T> getPropertyBinding(Class<T> propertyClass, String property, T defaultValue) {
        FieldPath fp = entity.getDtClass().getFieldPathForName(property);
        if (fp == null) {
            return Bindings.createObjectBinding(() -> defaultValue);
        } else {
            ObjectBinding<T> ob = getPropertyForFieldPath(fp).rawProperty();
            return Bindings.createObjectBinding(() -> ob.get(), ob);
        }
    }

    private void ensureChangeOpen() {
        if (!changeActive) {
            changeActive = true;
            beginChange();
        }
    }

    void commitChange() {
        if (changeActive) {
            changeActive = false;
            endChange();
        }
        for (ObservableEntityProperty property : properties) {
            if (property.isDirty()) {
                property.valueProperty().invalidate();
                property.rawProperty().invalidate();
                property.setDirty(false);
            }
        }
    }

    void update(FieldPath[] fieldPaths, int num) {
        for (int i = 0; i < num; i++) {
            int idx = getIndexForFieldPath(fieldPaths[i]);
            if (idx < 0) {
                ensureChangeOpen();
                idx = - idx - 1;
                indices.add(idx, fieldPaths[i]);
                properties.add(idx, new ObservableEntityProperty(entity, fieldPaths[i]));
                nextAdd(idx, idx);
            } else {
                properties.get(idx).setDirty(true);
            }
        }
    }

    public String getIndex() {
        return index.get();
    }

    public StringProperty indexProperty() {
        return index;
    }

    public void setIndex(String index) {
        this.index.set(index);
    }

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

    public StringProperty nameProperty() {
        return name;
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public Entity getEntity() {
        return entity;
    }

    @Override
    public String toString() {
        return String.format("%s(%s)", getName(), getIndex());
    }
}

@@ -401,16 +401,17 @@
 llection
+s
 ;%0Aimport
@@ -417,35 +417,32 @@
 t java.util.
-Collections
+Iterator
 ;%0Aimport jav
@@ -1084,26 +1084,24 @@
         
-Collection
+Iterator
 %3CFieldPa
@@ -1104,26 +1104,20 @@
 ldPath%3E 
-fieldPaths
+iter
  = entit
@@ -1133,77 +1133,101 @@
 e().
-collectFieldPaths();%0A        for (FieldPath fieldPath : fieldPaths) %7B
+fieldPathIterator();%0A        while (iter.hasNext()) %7B%0A            FieldPath fp = iter.next();
 %0A   
@@ -1248,24 +1248,17 @@
 es.add(f
-ieldPath
+p
 );%0A     
@@ -1317,24 +1317,17 @@
 ntity, f
-ieldPath
+p
 ));%0A    

make EntityState return an iterator over fieldpaths, instead of a list