//! Translates the context getter instructions.

use inkwell::values::BasicValue;

use revive_common::BIT_LENGTH_ETH_ADDRESS;

use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;

/// Translates the `gas_limit` instruction.
pub fn gas_limit<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let gas_limit_value = context
        .build_runtime_call(revive_runtime_api::polkavm_imports::GAS_LIMIT, &[])
        .expect("the gas_limit syscall method should return a value")
        .into_int_value();

    Ok(context
        .builder()
        .build_int_z_extend(gas_limit_value, context.word_type(), "gas_limit")?
        .into())
}

/// Translates the `gas_price` instruction.
pub fn gas_price<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let gas_price_value = context
        .build_runtime_call(revive_runtime_api::polkavm_imports::GAS_PRICE, &[])
        .expect("the gas_price syscall method should return a value")
        .into_int_value();

    Ok(context
        .builder()
        .build_int_z_extend(gas_price_value, context.word_type(), "gas_price")?
        .into())
}

/// Translates the `tx.origin` instruction.
pub fn origin<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let address_type = context.integer_type(BIT_LENGTH_ETH_ADDRESS);
    let address_pointer: Pointer<'_> = context
        .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
        .into();
    context.build_store(address_pointer, address_type.const_zero())?;
    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::ORIGIN,
        &[address_pointer.to_int(context).into()],
    );
    context.build_load_address(address_pointer)
}

/// Translates the `chain_id` instruction.
pub fn chain_id<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::CHAIN_ID)
}

/// Translates the `block_number` instruction.
pub fn block_number<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BLOCK_NUMBER)
}

/// Translates the `block_timestamp` instruction.
pub fn block_timestamp<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::NOW)
}

/// Translates the `block_hash` instruction.
pub fn block_hash<'ctx>(
    context: &mut Context<'ctx>,
    index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let output_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_out_ptr");
    let index_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr");
    context.build_store(index_pointer, index)?;

    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::BLOCK_HASH,
        &[
            index_pointer.to_int(context).into(),
            output_pointer.to_int(context).into(),
        ],
    );
    context.build_byte_swap(context.build_load(output_pointer, "block_hash")?)
}

/// Translates the `difficulty` instruction.
pub fn difficulty<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    Ok(context.word_const(2500000000000000).as_basic_value_enum())
}

/// Translates the `coinbase` instruction.
pub fn coinbase<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let pointer: Pointer<'_> = context
        .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
        .into();
    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::BLOCK_AUTHOR,
        &[pointer.to_int(context).into()],
    );
    context.build_load_address(pointer)
}

/// Translates the `basefee` instruction.
pub fn basefee<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BASE_FEE)
}

/// Translates the `address` instruction.
pub fn address<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let pointer: Pointer<'_> = context
        .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
        .into();
    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::ADDRESS,
        &[pointer.to_int(context).into()],
    );
    context.build_load_address(pointer)
}

/// Translates the `caller` instruction.
pub fn caller<'ctx>(
    context: &mut Context<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
    let pointer: Pointer<'_> = context
        .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)?
        .into();
    context.build_runtime_call(
        revive_runtime_api::polkavm_imports::CALLER,
        &[pointer.to_int(context).into()],
    );
    context.build_load_address(pointer)
}
