package ru.dbotthepony.mc.otm.block.tech

import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.SectionPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.EntityBlock
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockBehaviour
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock
import ru.dbotthepony.mc.otm.block.addSimpleDescription
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.core.collect.SupplierList
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.math.times
import ru.dbotthepony.mc.otm.core.runInBackground
import ru.dbotthepony.mc.otm.oncePre
import ru.dbotthepony.mc.otm.registry.game.MBlockEntities
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.shapes.BlockShapes
import kotlin.math.PI

private val props = BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(3f, 600.0f)

class BlockGravitationStabilizer : RotatableMatteryBlock(props), EntityBlock {
	init {
		addSimpleDescription()
		tooltips.needsNoPower(ChatFormatting.DARK_GRAY)
		addSimpleDescription("2", ChatFormatting.DARK_GRAY)
		addSimpleDescription("3", ChatFormatting.DARK_GRAY)
	}

	override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity {
		return GravitationStabilizerBlockEntity(p_153215_, p_153216_)
	}

	override fun <T : BlockEntity?> getTicker(
		p_153212_: Level,
		p_153213_: BlockState,
		p_153214_: BlockEntityType<T>
	): BlockEntityTicker<T>? {
		if (p_153212_.isClientSide || p_153214_ !== MBlockEntities.GRAVITATION_STABILIZER)
			return null

		return BlockEntityTicker { level, _, _, tile -> if (tile is GravitationStabilizerBlockEntity) tile.tick(level) }
	}

	override fun rotationFreedom(): BlockRotationFreedom {
		return BlockRotationFreedom.DIRECTIONAL
	}

	override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
		super.createBlockStateDefinition(builder)
		builder.add(WorkerState.SEMI_WORKER_STATE)
	}

	override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
		var state = super.getStateForPlacement(context) ?: return null
		val blockPos = context.clickedPos
		val level = context.level

		for (face in BlockRotationFreedom.DIRECTIONAL.possibleValues) {
			val dir = face.normal

			for (i in 1 ..GravitationStabilizerBlockEntity.RANGE) {
				val pos = blockPos + dir * i
				val chunk = level.chunkSource.getChunkNow(SectionPos.blockToSectionCoord(pos.x), SectionPos.blockToSectionCoord(pos.z)) ?: continue
				val getState = chunk.getBlockState(pos)

				if (!getState.isAir) {
					if (chunk.getBlockEntity(pos) is BlackHoleBlockEntity) {
						state = state.setValue(BlockRotationFreedom.DIRECTIONAL.property, face)
						break
					}
				}
			}
		}

		val bbPos = blockPos + state.getValue(BlockRotationFreedom.DIRECTIONAL.property).normal
		if (!context.level.isInWorldBounds(bbPos)) return null

		if (!level.getBlockState(bbPos).canBeReplaced(context)) return null

		return state
	}

	override fun setPlacedBy(
		level: Level,
		blockPos: BlockPos,
		blockState: BlockState,
		entity: LivingEntity?,
		itemStack: ItemStack
	) {
		super.setPlacedBy(level, blockPos, blockState, entity, itemStack)

		if (!level.isClientSide) {
			val bbPos = blockPos + blockState.getValue(BlockRotationFreedom.DIRECTIONAL.property).normal
			val newState = MBlocks.GRAVITATION_STABILIZER_LENS.defaultBlockState()
				.setValue(BlockRotationFreedom.DIRECTIONAL.property, blockState.getValue(BlockRotationFreedom.DIRECTIONAL.property))

			level.setBlock(bbPos, newState, UPDATE_ALL)
		}
	}

	override fun neighborChanged(
		state: BlockState,
		level: Level,
		pos: BlockPos,
		neighbour: Block,
		neighbourPos: BlockPos,
		movedByPiston: Boolean
	) {
		super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)

		level.oncePre {
			if (level.getBlockState(pos).block !is BlockGravitationStabilizer) return@oncePre

			val bb = getBoundingBlock(level, state, pos)
			if (bb.block !is BlockGravitationStabilizerLens) {
				level.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_ALL)
			}
		}
	}

	override fun getShape(
		p_60555_: BlockState,
		p_60556_: BlockGetter,
		p_60557_: BlockPos,
		p_60558_: CollisionContext
	): VoxelShape {
		return shapes[p_60555_[BlockRotationFreedom.DIRECTIONAL].ordinal]
	}

	companion object {
		private val shapes = SupplierList.ofFuture(
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.rotateAroundX(PI / 2).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.rotateAroundX(-PI / 2).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.rotateFromSouth(Direction.NORTH).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.rotateFromSouth(Direction.WEST).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER.rotateFromSouth(Direction.EAST).computeShape() },
		)

		fun getBoundingBlock(level: Level, blockState: BlockState, blockPos: BlockPos): BlockState {
			return level.getBlockState(getBoundingBlockPos(blockState, blockPos))
		}

		fun getBoundingBlockPos(blockState: BlockState, blockPos: BlockPos): BlockPos {
			return blockPos + blockState.getValue(BlockRotationFreedom.DIRECTIONAL.property).normal
		}
	}
}

class BlockGravitationStabilizerLens : RotatableMatteryBlock(props) {
	override fun rotationFreedom(): BlockRotationFreedom {
		return BlockRotationFreedom.DIRECTIONAL
	}

	override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
		super.createBlockStateDefinition(builder)
		builder.add(WorkerState.SEMI_WORKER_STATE)
	}

	override fun neighborChanged(
		state: BlockState,
		level: Level,
		pos: BlockPos,
		neighbour: Block,
		neighbourPos: BlockPos,
		movedByPiston: Boolean
	) {
		super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)

		level.oncePre {
			if (level.getBlockState(pos).block !is BlockGravitationStabilizerLens) return@oncePre

			val bb = getBoundingBlock(level, state, pos)
			if (bb.block !is BlockGravitationStabilizer) {
				level.setBlock(pos, Blocks.AIR.defaultBlockState(), UPDATE_ALL)
			}
		}
	}

	override fun getShape(
		p_60555_: BlockState,
		p_60556_: BlockGetter,
		p_60557_: BlockPos,
		p_60558_: CollisionContext
	): VoxelShape {
		return shapes[p_60555_[BlockRotationFreedom.DIRECTIONAL].front.ordinal]
	}

	companion object {
		fun getBoundingBlock(level: Level, blockState: BlockState, blockPos: BlockPos): BlockState {
			return level.getBlockState(getBoundingBlockPos(blockState, blockPos))
		}

		fun getBoundingBlockPos(blockState: BlockState, blockPos: BlockPos): BlockPos {
			return blockPos + blockState.getValue(BlockRotationFreedom.DIRECTIONAL.property).front.opposite.normal
		}

		private val shapes = SupplierList.ofFuture(
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.rotateAroundX(PI / 2).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.rotateAroundX(-PI / 2).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.rotateFromSouth(Direction.NORTH).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.rotateFromSouth(Direction.WEST).computeShape() },
			runInBackground { BlockShapes.GRAVITATION_STABILIZER_LENS.rotateFromSouth(Direction.EAST).computeShape() },
		)
	}
}
