package ru.dbotthepony.mc.otm.menu.input

import net.minecraft.world.entity.player.Player
import ru.dbotthepony.kommons.util.Delegate
import ru.dbotthepony.kommons.util.Listenable
import ru.dbotthepony.kommons.util.ListenableDelegate
import ru.dbotthepony.mc.otm.menu.MatteryMenu
import java.util.function.Consumer
import java.util.function.Predicate
import java.util.function.Supplier
import kotlin.reflect.KMutableProperty0

/**
 * Represents Server to Client synchronization and Client to Server input
 *
 * Getting and setting values should ONLY be done clientside
 */
interface IPlayerInputWithFeedback<V> : ListenableDelegate<V>, Predicate<Player> {
	private class Map<N, V>(private val parent: IPlayerInputWithFeedback<V>, private val from: (V) -> N, private val to: (N) -> V): IPlayerInputWithFeedback<N> {
		override fun accept(t: N) {
			parent.accept(to(t))
		}

		override fun addListener(listener: Consumer<N>): Listenable.L {
			return parent.addListener(Consumer { listener.accept(from(it)) })
		}

		override fun get(): N {
			return from(parent.get())
		}

		override fun test(t: Player): Boolean {
			return parent.test(t)
		}
	}

	fun <N> map(from: (V) -> N, to: (N) -> V): IPlayerInputWithFeedback<N> {
		return Map(this, from, to)
	}
}

/**
 * Represents Server to Client synchronization and Client to Server input
 *
 * Getting and setting values should ONLY be done clientside
 */
abstract class AbstractPlayerInputWithFeedback<V> : IPlayerInputWithFeedback<V> {
	abstract val input: MatteryMenu.PlayerInput<V>
	abstract val field: ListenableDelegate<V>

	final override fun addListener(listener: Consumer<V>): Listenable.L {
		return field.addListener(listener)
	}

	val value: V get() = this.field.get()

	final override fun get(): V {
		return value
	}

	final override fun accept(t: V) {
		input.accept(t)
	}

	override fun test(player: Player) = input.test(player)
	fun filter(filter: Predicate<Player>) = input.filter(filter)

	var supplier: (() -> V)? = null
	var consumer: ((V) -> Unit)? = null

	fun withSupplier(func: () -> V): AbstractPlayerInputWithFeedback<V> {
		supplier = func
		return this
	}

	fun withSupplier(func: Supplier<V>): AbstractPlayerInputWithFeedback<V> {
		supplier = func::get
		return this
	}

	fun withConsumer(func: (V) -> Unit): AbstractPlayerInputWithFeedback<V> {
		consumer = func
		return this
	}

	fun withConsumer(func: Consumer<V>): AbstractPlayerInputWithFeedback<V> {
		consumer = func::accept
		return this
	}

	fun with(state: KMutableProperty0<V>): AbstractPlayerInputWithFeedback<V> {
		withConsumer { state.set(it) }
		withSupplier { state.get() }
		return this
	}

	fun with(state: Delegate<V>): AbstractPlayerInputWithFeedback<V> {
		withConsumer(state)
		withSupplier(state)
		return this
	}

	fun clear(): AbstractPlayerInputWithFeedback<V> {
		supplier = null
		consumer = null
		return this
	}
}
