package ru.dbotthepony.mc.otm.core.multiblock

import com.google.common.collect.ImmutableSet
import net.minecraft.core.BlockPos
import net.minecraft.core.Vec3i
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.core.math.plus

fun interface BlockPredicate {
	fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean

	fun rotate(rotation: Rotation): BlockPredicate {
		return this
	}

	fun and(other: BlockPredicate): BlockPredicate {
		return BlockPredicate { pos, access, blockState, blockEntity -> test(pos, access, blockState, blockEntity) && other.test(pos, access, blockState, blockEntity) }
	}

	fun or(other: BlockPredicate): BlockPredicate {
		return BlockPredicate { pos, access, blockState, blockEntity -> test(pos, access, blockState, blockEntity) || other.test(pos, access, blockState, blockEntity) }
	}

	fun and(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
		return And(ImmutableSet.copyOf(listOf(other, *others)))
	}

	fun or(other: BlockPredicate, vararg others: BlockPredicate): BlockPredicate {
		return Or(ImmutableSet.copyOf(listOf(other, *others)))
	}

	fun offset(offset: BlockPos): Positioned {
		return Positioned(offset, this)
	}

	fun offset(offset: Vec3i): Positioned {
		return offset(BlockPos(offset))
	}

	data class Positioned(val offset: BlockPos, val parent: BlockPredicate) : BlockPredicate {
		override fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean {
			return parent.test(offset + pos, access, blockState, blockEntity)
		}

		override fun offset(offset: BlockPos): Positioned {
			return Positioned(this.offset + offset, parent)
		}
	}

	data class And(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
		constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
		constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))

		override fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean {
			return nodes.all { it.test(pos, access, blockState, blockEntity) }
		}
	}

	data class Or(val nodes: ImmutableSet<BlockPredicate>) : BlockPredicate {
		constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
		constructor(nodes: Set<BlockPredicate>) : this(ImmutableSet.copyOf(nodes))

		override fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean {
			return nodes.any { it.test(pos, access, blockState, blockEntity) }
		}
	}

	object Air : BlockPredicate {
		override fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean {
			return blockState.isAir
		}
	}

	object NotAir : BlockPredicate {
		override fun test(pos: BlockPos, access: LevelAccessor, blockState: BlockState, blockEntity: Lazy<BlockEntity?>): Boolean {
			return !blockState.isAir
		}
	}
}
