
@file:Suppress("LeakingThis")

package ru.dbotthepony.mc.otm.block

import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.world.level.BlockGetter
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.BooleanProperty
import net.minecraft.world.level.material.MapColor
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.core.math.BlockRotation
import java.util.Collections
import java.util.EnumMap

abstract class CableBlock(properties: Properties) : Block(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
		)
	}

	companion object {
		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> = 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().sound(SoundType.METAL).strength(1.0f, 6.0f)),
	EntityBlock {
	private val CORE_SHAPE: VoxelShape = Shapes.box(
		0.5 - 0.15,
		0.5 - 0.15,
		0.5 - 0.15,
		0.5 + 0.15,
		0.5 + 0.15,
		0.5 + 0.15
	)

	private val shapes = getShapeForEachState {
		val shapes = ArrayList<VoxelShape>()
		val width = 0.15

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

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

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

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

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

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

		var finalShape = CORE_SHAPE

		for (add_shape in shapes) {
			finalShape = Shapes.joinUnoptimized(finalShape, add_shape, BooleanOp.OR)
		}

		return@getShapeForEachState finalShape
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(
		p_60555_: BlockState,
		p_60556_: BlockGetter,
		p_60557_: BlockPos,
		p_60558_: CollisionContext
	): VoxelShape {
		return shapes[p_60555_] ?: CORE_SHAPE
	}

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

class StorageCableBlock : CableBlock(
	Properties.of().mapColor(MapColor.METAL).requiresCorrectToolForDrops().sound(SoundType.METAL).strength(1.0f, 6.0f)),
	EntityBlock {

	companion object {
		val CORE_SHAPE: VoxelShape = Shapes.box(
			0.5 - 0.185,
			0.5 - 0.185,
			0.5 - 0.185,
			0.5 + 0.185,
			0.5 + 0.185,
			0.5 + 0.185
		)

		private const val width = 0.185

		fun getShapeFor(it: BlockState): MutableList<VoxelShape> {
			val shapes = ArrayList<VoxelShape>()

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

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

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

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

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

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

			shapes.add(CORE_SHAPE)
			return shapes
		}
	}

	private val shapes = getShapeForEachState {
		val shapes = getShapeFor(it)
		var finalShape = shapes[0]

		for (i in 1 until shapes.size) {
			finalShape = Shapes.joinUnoptimized(finalShape, shapes[i], BooleanOp.OR)
		}

		return@getShapeForEachState finalShape
	}

	@Suppress("OVERRIDE_DEPRECATION")
	override fun getShape(
		p_60555_: BlockState,
		p_60556_: BlockGetter,
		p_60557_: BlockPos,
		p_60558_: CollisionContext
	): VoxelShape {
		return shapes[p_60555_] ?: CORE_SHAPE
	}

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