package ovh.glitchlabs.ornamentum.blocks.custom;

import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
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.phys.BlockHitResult;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;

public class LinkBlock extends DirectionalBlock {

    public static final MapCodec<LinkBlock> CODEC = simpleCodec(LinkBlock::new);

    public static final BooleanProperty ON = BooleanProperty.create("on");

    private static final int MAX_NETWORK_SIZE = 4096;

    public LinkBlock(Properties properties) {
        super(properties);
        this.registerDefaultState(this.stateDefinition.any()
                .setValue(ON, false)
                .setValue(FACING, Direction.NORTH));
    }

    @Override
    protected MapCodec<? extends DirectionalBlock> codec() {
        return CODEC;
    }


    @Override
    protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
        super.createBlockStateDefinition(builder);
        builder.add(FACING);
        builder.add(ON);
    }

    @Override
    public BlockState getStateForPlacement(BlockPlaceContext context) {
        BlockState state = super.getStateForPlacement(context);
        if (state == null)
            return null;
        return state.setValue(FACING, context.getClickedFace()).setValue(ON, false);
    }

    public BlockState rotate(BlockState state, Rotation rot) {
        return state.setValue(FACING, rot.rotate(state.getValue(FACING)));
    }

    public BlockState mirror(BlockState state, Mirror mirrorIn) {
        return state.rotate(mirrorIn.getRotation(state.getValue(FACING)));
    }


    @Override
    protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
        if (player.isShiftKeyDown()) {
            if (!level.isClientSide) {
                toggleNetwork(level, pos, !state.getValue(ON));
            }
            return InteractionResult.SUCCESS;
        }
        return InteractionResult.PASS;
    }


    private void toggleNetwork(Level level, BlockPos origin, boolean newState) {
        Set<BlockPos> visited = new HashSet<>();
        Deque<BlockPos> queue = new ArrayDeque<>();

        visited.add(origin.immutable());
        queue.add(origin);

        while (!queue.isEmpty() && visited.size() <= MAX_NETWORK_SIZE) {
            BlockPos pos = queue.poll();
            BlockState state = level.getBlockState(pos);

            if (!(state.getBlock() instanceof LinkBlock)) {
                continue;
            }

            if (state.getValue(ON) != newState) {
                level.setBlock(pos, state.setValue(ON, newState), Block.UPDATE_ALL);
            }

            for (Direction direction : Direction.values()) {
                BlockPos neighborPos = pos.relative(direction);
                if (visited.contains(neighborPos)) {
                    continue;
                }

                BlockState neighborState = level.getBlockState(neighborPos);
                if (neighborState.getBlock() instanceof LinkBlock) {
                    visited.add(neighborPos.immutable());
                    queue.add(neighborPos);
                }
            }
        }
    }
}
