package ru.dbotthepony.mc.otm.block.decorative

import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.TooltipFlag
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.*
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BlockStateProperties
import net.minecraft.world.level.material.MapColor
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.config.ServerConfig
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom
import ru.dbotthepony.mc.otm.core.math.blockRotation
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.math.unaryMinus
import ru.dbotthepony.mc.otm.once
import ru.dbotthepony.mc.otm.registry.MBlocks

private val FACING_FULL = BlockRotationFreedom.TWO.property

class LaboratoryLampLight : Block(Properties.of().strength(-1.0F, 3600000.8F).noCollission().noLootTable().replaceable().lightLevel { 15 }) {
	override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
		super.createBlockStateDefinition(builder)
		builder.add(FACING_FULL)
	}

	override fun hasDynamicShape(): Boolean {
		return true
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(
		p_60555_: BlockState,
		p_60556_: BlockGetter,
		p_60557_: BlockPos,
		p_60558_: CollisionContext
	): VoxelShape {
		return if (p_60558_.isHoldingItem(Items.LIGHT)) Shapes.block() else Shapes.empty()
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
		@Suppress("DEPRECATION")
		return super.rotate(blockState, rotation).setValue(FACING_FULL, blockState[FACING_FULL].rotate(rotation))
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getRenderShape(p_60550_: BlockState): RenderShape {
		return RenderShape.INVISIBLE
	}

	override fun propagatesSkylightDown(p_49928_: BlockState, p_49929_: BlockGetter, p_49930_: BlockPos): Boolean {
		return true
	}

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

		level.once {
			if (level.getBlockState(pos) != state)
				return@once

			val facing = state[FACING_FULL]

			var hit = false

			for (i in 1 .. ServerConfig.LABORATORY_LAMP_LIGHT_LENGTH) {
				val target = pos + facing * i
				val targetState = level.getBlockState(target)

				if (targetState.block is LaboratoryLamp && targetState[BlockStateProperties.LIT]) {
					hit = true
					(targetState.block as LaboratoryLamp).doTick(targetState, level, target)
					break
				} else if (targetState.block != MBlocks.LABORATORY_LAMP_LIGHT && !targetState.isAir) {
					break
				}
			}

			if (!hit) {
				level.setBlockAndUpdate(pos, Blocks.AIR.defaultBlockState())
			}
		}
	}
}

class LaboratoryLamp(val invertRedstone: Boolean) : Block(Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).explosionResistance(12f).destroyTime(2f).requiresCorrectToolForDrops()) {
	init {
		registerDefaultState(stateDefinition.any().setValue(BlockStateProperties.LIT, !invertRedstone))
	}

	override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
		super.createBlockStateDefinition(builder)
		builder.add(FACING_FULL)
		builder.add(BlockStateProperties.LIT)
	}

	override fun getStateForPlacement(context: BlockPlaceContext): BlockState {
		return super.getStateForPlacement(context)!!
			.setValue(BlockStateProperties.LIT, !invertRedstone)
			.setValue(FACING_FULL, context.nearestLookingDirection.blockRotation.oppositeFront)
	}

	override fun appendHoverText(
		p_49816_: ItemStack,
		p_49817_: BlockGetter?,
		p_49818_: MutableList<Component>,
		p_49819_: TooltipFlag
	) {
		super.appendHoverText(p_49816_, p_49817_, p_49818_, p_49819_)
		p_49818_.add(TranslatableComponent("${MBlocks.LABORATORY_LAMP.descriptionId}.description").withStyle(ChatFormatting.GRAY))
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun onPlace(
		state: BlockState,
		level: Level,
		pos: BlockPos,
		oldState: BlockState,
		movedByPiston: Boolean
	) {
		super.onPlace(state, level, pos, oldState, movedByPiston)

		doTick(state, level, pos)
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun rotate(blockState: BlockState, rotation: Rotation): BlockState {
		@Suppress("DEPRECATION")
		return super.rotate(blockState, rotation).setValue(FACING_FULL, blockState[FACING_FULL].rotate(rotation))
	}

	fun doTick(
		state: BlockState,
		level: Level,
		pos: BlockPos,
	) {
		level.once {
			if (level.getBlockState(pos) != state)
				return@once

			val lit = state[BlockStateProperties.LIT]
			val shouldBeLit: Boolean

			if (invertRedstone) {
				shouldBeLit = level.hasNeighborSignal(pos)
			} else {
				shouldBeLit = !level.hasNeighborSignal(pos)
			}

			if (lit != shouldBeLit) {
				level.setBlockAndUpdate(pos, state.setValue(BlockStateProperties.LIT, shouldBeLit))
			}

			val facing = state[FACING_FULL]

			for (i in 1 .. ServerConfig.LABORATORY_LAMP_LIGHT_LENGTH) {
				val target = pos + facing * i
				val targetState = level.getBlockState(target)

				if (!targetState.isAir && targetState.block != MBlocks.LABORATORY_LAMP_LIGHT) {
					break
				}

				if (shouldBeLit) {
					if (targetState.isAir && targetState.block != MBlocks.LABORATORY_LAMP_LIGHT) {
						level.setBlockAndUpdate(target, MBlocks.LABORATORY_LAMP_LIGHT.defaultBlockState()
							.setValue(FACING_FULL, facing.oppositeFront)
						)
					}
				} else {
					if (targetState.block == MBlocks.LABORATORY_LAMP_LIGHT) {
						level.setBlockAndUpdate(target, Blocks.AIR.defaultBlockState())
					}
				}
			}
		}
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun neighborChanged(
		state: BlockState,
		level: Level,
		pos: BlockPos,
		neighbour: Block,
		neighbourPos: BlockPos,
		movedByPiston: Boolean
	) {
		super.neighborChanged(state, level, pos, neighbour, neighbourPos, movedByPiston)
		doTick(state, level, pos)
	}
}
