package com.gx.obe.bind.prop;

import java.util.Objects;
import java.util.UUID;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import com.gx.obe.bind.base.P;
import com.gx.obe.bind.view.ChangeListener;
import com.gx.obe.bind.view.MView;
import com.gx.obe.bind.view.Read;
import com.gx.obe.bind.view.View;
import com.gx.obe.bind.view.Write;

public class Prop<T> extends P<T> implements MView<T> {
	
	private Lock lock = new Lock();
	
	public Prop() {}
	
	public Prop(T value) {
		set(value);
	}
	
	public Prop<T> orElseGet(Supplier<T> other) {
		super.orElseGet(other);
		return this;
	}
	
	public Prop<T> orElse(T defaultValue) {
		super.orElse(defaultValue);
		return this;
	}
	
	@Override
	public Prop<T> as(String name) {
		super.as(name);
		return this;
	}
	
	@Override
	public Prop<T> limit(BiPredicate<T, T> predicate) {
		super.limit(predicate);
		return this;
	}
	
	public boolean isLock() {
		return lock.isLock();
	}
	
	public void replace(Function<T, T> function) {
		set(Objects.requireNonNull(function).apply(get()));
	}
	
	public void changer(Consumer<T> consumer) {
		Objects.requireNonNull(consumer).accept(get());
		set(get());
	}
	
	public void bindBidirectional(MView<T> mView) {
		Objects.requireNonNull(mView);
		String key = UUID.randomUUID().toString();
		bind(key, mView);
		mView.addChangeListener(() -> set(mView.get(), key));
	}
	
	public void bindBidirectional(Write<T> write, Read<T> read, View model) {
		Objects.requireNonNull(write);
		Objects.requireNonNull(read);
		Objects.requireNonNull(model);
		
		String key = UUID.randomUUID().toString();
		bind(key, write);
		model.addChangeListener(() -> set(read.get(), key));
	}
	
	@Override
	public void set(T value) {
		lock.sync(() -> change(value));
	}
	
	private void set(T value, String key) {
		lock.sync(() -> change(value, key));
	}
	
}

class Lock {
	private boolean lock = false;
	
	public boolean isLock() {
		return lock;
	}
	
	public void sync(ChangeListener changeListener) {
		if (lock) return;
		lock = true;
		changeListener.change();
		lock = false;
	}
}