package com.ecep.contract.vm;

import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

@Data
public class BaseViewModel<T> {
    private SimpleBooleanProperty changed = new SimpleBooleanProperty(false);
    @Setter(AccessLevel.PROTECTED)
    @Getter(AccessLevel.PROTECTED)
    private boolean skipChanged = false;
    private InvalidationListener changedListener = observable -> {
//        System.out.println("observable = " + observable);
        if (skipChanged) {
            return;
        }
        changed.set(true);
    };

    public boolean isChanged() {
        return changed.get();
    }

    public void setChanged(boolean changed) {
        this.changed.set(changed);
    }

    public void bindListener() {
        for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(getClass())) {
            String name = descriptor.getName();
            if (name.equals("changed")) {
                continue;
            }
            Class<?> propertyType = descriptor.getPropertyType();
            if (Property.class.isAssignableFrom(propertyType)) {
                try {
                    Property<?> observable = (Property<?>) descriptor.getReadMethod().invoke(this);
                    observable.addListener(changedListener);
                } catch (IllegalAccessException | InvocationTargetException ignored) {

                }
            }
        }
    }

    public void unBindListener() throws InvocationTargetException, IllegalAccessException {
        for (PropertyDescriptor descriptor : BeanUtils.getPropertyDescriptors(getClass())) {
            String name = descriptor.getName();
            if (name.equals("changed")) {
                continue;
            }
            Class<?> propertyType = descriptor.getPropertyType();
            if (Property.class.isAssignableFrom(propertyType)) {
                Observable observable = (Observable) descriptor.getReadMethod().invoke(this);
                observable.removeListener(changedListener);
            }
        }
    }

    /**
     * 应用了 changed property 的控制
     * <p>
     * 继承类需要重写 updateImpl 方法即可
     */
    public final BaseViewModel<T> update(T v) {
        setSkipChanged(true);
        updateFrom(v);
        setChanged(false);
        setSkipChanged(false);
        return this;
    }

    protected void updateFrom(T v) {
    }

    public boolean copyTo(T v) {
        return false;
    }

    /**
     * 在UI线程中更新
     */
    public static <T, TV extends BaseViewModel<T>> void updateInFxApplicationThread(T entity, TV vm) {
        if (Platform.isFxApplicationThread()) {
            vm.update(entity);
        } else {
            Platform.runLater(() -> vm.update(entity));
        }
    }
}
