use crate::block::blocks::plant::PlantBlockBase;
use crate::block::{
    BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, CanPlaceAtArgs,
    GetStateForNeighborUpdateArgs, PlacedArgs,
};
use pumpkin_data::BlockStateId;
use pumpkin_data::{Block, BlockId};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::{BlockAccessor, BlockFlags};

pub struct TwistingVinesBlock;
impl BlockMetadata for TwistingVinesBlock {
    fn ids() -> Box<[BlockId]> {
        [BlockId::TWISTING_VINES, BlockId::TWISTING_VINES_PLANT].into()
    }
}

impl BlockBehaviour for TwistingVinesBlock {
    fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
        <Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position)
    }
    fn get_state_for_neighbor_update<'a>(
        &'a self,
        args: GetStateForNeighborUpdateArgs<'a>,
    ) -> BlockFuture<'a, BlockStateId> {
        Box::pin(async move {
            <Self as PlantBlockBase>::get_state_for_neighbor_update(
                self,
                args.world,
                args.position,
                args.state_id,
            )
            .await
        })
    }
    fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
        Box::pin(async move {
            let support_pos = args.position.down();
            let support_block = args.world.get_block(&support_pos);
            if support_block == &Block::TWISTING_VINES {
                args.world
                    .set_block_state(
                        &support_pos,
                        Block::TWISTING_VINES_PLANT.default_state.id,
                        BlockFlags::empty(),
                    )
                    .await;
            }
        })
    }
    fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
        Box::pin(async move {
            let support_pos = args.position.down();
            let support_block = args.world.get_block(&support_pos);
            if support_block == &Block::TWISTING_VINES_PLANT {
                args.world
                    .set_block_state(
                        &support_pos,
                        Block::TWISTING_VINES.default_state.id,
                        BlockFlags::empty(),
                    )
                    .await;
            }
        })
    }
}

impl PlantBlockBase for TwistingVinesBlock {
    fn can_plant_on_top(
        &self,
        block_accessor: &dyn pumpkin_world::world::BlockAccessor,
        pos: &pumpkin_util::math::position::BlockPos,
    ) -> bool {
        // Determine support block
        let support_pos = pos;
        let (support_block, support_block_state) = block_accessor.get_block_and_state(support_pos);

        if support_block == &Block::TWISTING_VINES || support_block == &Block::TWISTING_VINES_PLANT
        {
            return true;
        }
        if support_block_state.is_side_solid(pumpkin_data::BlockDirection::Down)
            && support_block.is_solid()
        {
            return true;
        }
        false
    }
    async fn get_state_for_neighbor_update(
        &self,
        block_accessor: &dyn BlockAccessor,
        block_pos: &BlockPos,
        block_state: BlockStateId,
    ) -> BlockStateId {
        if !<Self as PlantBlockBase>::can_place_at(self, block_accessor, block_pos) {
            return Block::AIR.default_state.id;
        }
        block_state
    }
}
