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

import net.minecraft.world.entity.player.Player
import ru.dbotthepony.mc.otm.client.minecraft
import ru.dbotthepony.mc.otm.core.GetterSetter
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> : GetterSetter<V>, Predicate<Player?> {
	companion object {
		fun <V> of(getterSetter: GetterSetter<V>): IPlayerInputWithFeedback<V> {
			return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter {
				override fun test(t: Player?): Boolean {
					return true
				}
			}
		}

		fun <V> of(getterSetter: GetterSetter<V>, filter: Predicate<Player?>): IPlayerInputWithFeedback<V> {
			return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter, Predicate<Player?> by filter {}
		}

		fun <V> validPlayer(getterSetter: GetterSetter<V>): IPlayerInputWithFeedback<V> {
			return object : IPlayerInputWithFeedback<V>, GetterSetter<V> by getterSetter {
				override fun test(t: Player?): Boolean {
					return t != null
				}
			}
		}
	}
}

fun <V> GetterSetter<V>.wrapAsPlayerInput(filter: Predicate<Player?> = Predicate { it != null }): IPlayerInputWithFeedback<V> {
	return IPlayerInputWithFeedback.of(this, filter)
}

/**
 * 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 value: V

	override fun get(): V {
		return value
	}

	override fun accept(t: V) {
		input.input(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: GetterSetter<V>): AbstractPlayerInputWithFeedback<V> {
		withConsumer(state)
		withSupplier(state)
		return this
	}

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