package ru.dbotthepony.mc.otm.core

import java.util.function.Consumer
import java.util.function.Supplier
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty

inline var <V> GetterSetter<V>.value: V
	get() = get()
	set(value) { accept(value) }

interface GetterSetter<V> : Supplier<V>, Consumer<V>, ReadWriteProperty<Any?, V> {
	override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
		accept(value)
	}

	override fun getValue(thisRef: Any?, property: KProperty<*>): V {
		return get()
	}

	operator fun invoke(): V {
		return get()
	}

	operator fun invoke(value: V) {
		accept(value)
	}

	fun asGetterOnly(): GetterSetter<V> {
		val self = this

		return object : GetterSetter<V> {
			override fun get(): V {
				return self.get()
			}

			override fun accept(t: V) {
			}
		}
	}

	fun watch(watch: (old: V, new: V) -> Unit): GetterSetter<V> {
		val self = this

		return object : GetterSetter<V> {
			override fun get(): V {
				return self.get()
			}

			override fun accept(t: V) {
				val old = get()
				self.accept(t)
				watch.invoke(old, t)
			}
		}
	}

	companion object {
		fun <V> of(getter: Supplier<V>, setter: Consumer<V>): GetterSetter<V> {
			return object : GetterSetter<V> {
				override fun get(): V {
					return getter.get()
				}

				override fun accept(t: V) {
					setter.accept(t)
				}
			}
		}

		fun <V> of(getter: () -> V, setter: (V) -> Unit): GetterSetter<V> {
			return object : GetterSetter<V> {
				override fun get(): V {
					return getter.invoke()
				}

				override fun accept(t: V) {
					setter.invoke(t)
				}
			}
		}

		fun <V> of(property: KMutableProperty0<V>): GetterSetter<V> {
			return object : GetterSetter<V> {
				override fun get(): V {
					return property.get()
				}

				override fun accept(t: V) {
					property.set(t)
				}
			}
		}

		fun <V> box(value: V): GetterSetter<V> {
			return object : GetterSetter<V> {
				private var value = value

				override fun get(): V {
					return value
				}

				override fun accept(t: V) {
					this.value = t
				}
			}
		}
	}
}

operator fun <T> Supplier<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
	return get()
}

operator fun <T> Consumer<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
	accept(value)
}

fun <V> KMutableProperty0<V>.asGetterSetter(watch: ((old: V, new: V) -> Unit)? = null): GetterSetter<V> {
	return GetterSetter.of(this).let {
		if (watch != null) {
			it.watch(watch)
		} else {
			it
		}
	}
}

fun <V> KMutableProperty0<V>.asGetterOnly() = GetterSetter.of(Supplier { this.get() }, Consumer { /* do nothing */ })
