
@file:Suppress("LeakingThis")

package ru.dbotthepony.mc.otm.block

import com.google.common.collect.ImmutableMap
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.player.Player
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.EntityBlock
import net.minecraft.world.level.block.SoundType
import net.minecraft.world.level.block.entity.BlockEntity
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.block.state.properties.BooleanProperty
import net.minecraft.world.level.material.MapColor
import net.minecraft.world.level.material.PushReaction
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.BooleanOp
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.block.entity.MatterCableBlockEntity
import ru.dbotthepony.mc.otm.block.entity.StorageCableBlockEntity
import ru.dbotthepony.mc.otm.block.entity.cable.EnergyCableBlockEntity
import java.util.Collections
import java.util.EnumMap

abstract class CableBlock(properties: Properties) : MatteryBlock(properties) {
	init {
		registerDefaultState(defaultBlockState()
			.setValue(CONNECTION_SOUTH, false)
			.setValue(CONNECTION_WEST, false)
			.setValue(CONNECTION_EAST, false)
			.setValue(CONNECTION_NORTH, false)
			.setValue(CONNECTION_UP, false)
			.setValue(CONNECTION_DOWN, false))
	}

	override fun hasDynamicShape() = true

	override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
		builder.add(
			CONNECTION_SOUTH,
			CONNECTION_WEST,
			CONNECTION_EAST,
			CONNECTION_NORTH,
			CONNECTION_UP,
			CONNECTION_DOWN
		)
	}

	protected fun generateShapes(halfCoreSize: Double): Map<BlockState, VoxelShape> {
		return getShapeForEachStateMattery { getShapeFor(it, halfCoreSize) }
	}

	override fun useWithoutItem(blockState: BlockState, level: Level, blockPos: BlockPos, ply: Player, blockHitResult: BlockHitResult): InteractionResult {
		return InteractionResult.PASS
	}

	companion object {
		fun getShapeFor(it: BlockState, halfCoreSize: Double): VoxelShape {
			val shapes = ArrayList<VoxelShape>()

			if (it.getValue(CONNECTION_SOUTH))
				shapes.add(Shapes.box(0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 + halfCoreSize, 0.5 + halfCoreSize, 0.5 + halfCoreSize, 1.0))

			if (it.getValue(CONNECTION_NORTH))
				shapes.add(Shapes.box(0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.0, 0.5 + halfCoreSize, 0.5 + halfCoreSize, 0.5 - halfCoreSize))

			if (it.getValue(CONNECTION_DOWN))
				shapes.add(Shapes.box(0.5 - halfCoreSize, 0.0, 0.5 - halfCoreSize, 0.5 + halfCoreSize, 0.5 - halfCoreSize, 0.5 + halfCoreSize))

			if (it.getValue(CONNECTION_UP))
				shapes.add(Shapes.box(0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 + halfCoreSize, 1.0, 0.5 + halfCoreSize))

			if (it.getValue(CONNECTION_EAST))
				shapes.add(Shapes.box(0.5 + halfCoreSize, 0.5 - halfCoreSize, 0.5 - halfCoreSize, 1.0, 0.5 + halfCoreSize, 0.5 + halfCoreSize))

			if (it.getValue(CONNECTION_WEST))
				shapes.add(Shapes.box(0.0, 0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 + halfCoreSize, 0.5 + halfCoreSize))

			var finalShape = Shapes.box(0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 - halfCoreSize, 0.5 + halfCoreSize, 0.5 + halfCoreSize, 0.5 + halfCoreSize)

			for (shape in shapes)
				finalShape = Shapes.join(finalShape, shape, BooleanOp.OR)

			return finalShape
		}

		val CONNECTION_SOUTH: BooleanProperty = BooleanProperty.create("connect_south")
		val CONNECTION_WEST: BooleanProperty = BooleanProperty.create("connect_west")
		val CONNECTION_EAST: BooleanProperty = BooleanProperty.create("connect_east")
		val CONNECTION_NORTH: BooleanProperty = BooleanProperty.create("connect_north")
		val CONNECTION_UP: BooleanProperty = BooleanProperty.create("connect_up")
		val CONNECTION_DOWN: BooleanProperty = BooleanProperty.create("connect_down")

		val MAPPING_CONNECTION_PROP: Map<Direction, BooleanProperty> = Collections.unmodifiableMap(EnumMap<Direction, BooleanProperty>(Direction::class.java)
			.let {
				it[Direction.DOWN] = CONNECTION_DOWN
				it[Direction.UP] = CONNECTION_UP
				it[Direction.NORTH] = CONNECTION_NORTH
				it[Direction.SOUTH] = CONNECTION_SOUTH
				it[Direction.WEST] = CONNECTION_WEST
				it[Direction.EAST] = CONNECTION_EAST
				Collections.unmodifiableMap(it)
			})
	}
}

class MatterCableBlock : CableBlock(Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().pushReaction(PushReaction.DESTROY).sound(SoundType.METAL).strength(1.0f, 6.0f)), EntityBlock {
	private val shapes = generateShapes(0.15)

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(blockState: BlockState, accessor: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
		return shapes[blockState] ?: Shapes.block()
	}

	override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
		return MatterCableBlockEntity(blockPos, blockState)
	}
}

class StorageCableBlock : CableBlock(Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().pushReaction(PushReaction.DESTROY).sound(SoundType.METAL).strength(1.0f, 6.0f)), EntityBlock {
	private val shapes = generateShapes(0.185)

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(blockState: BlockState, accessor: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
		return shapes[blockState] ?: Shapes.block()
	}

	override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
		return StorageCableBlockEntity(blockPos, blockState)
	}
}

class EnergyCableBlock(val factory: (blockPos: BlockPos, blockState: BlockState) -> BlockEntity) : CableBlock(Properties.of().mapColor(MapColor.METAL).pushReaction(PushReaction.DESTROY).requiresCorrectToolForDrops().sound(SoundType.METAL).strength(1.0f, 6.0f)), EntityBlock {
	private val shapes = generateShapes(0.185)

	init {
		registerDefaultState(defaultBlockState().setValue(BlockStateProperties.POWERED, false))
	}

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

		builder.add(BlockStateProperties.POWERED)
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(blockState: BlockState, accessor: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
		return shapes[blockState] ?: Shapes.block()
	}

	override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
		return factory(blockPos, blockState)
	}
}
