use core::arch::asm;

use crate::risc_ecall::Ecall;

pub unsafe fn log_txt(ptr: *const u8, len: usize) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::Console as usize,
            in("a0") ptr,
            in("a1") len,
            options(nostack)
        );
    }
}

pub unsafe fn write_result(d: *const u8, l: usize) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::WriteResult as usize,
            in("a0") d,
            in("a1") l,
            options(nostack)
        );
    }
}

pub unsafe fn storage_load_bytes32(key: *const u8, out: *mut u8) {
    unsafe {
        core::ptr::copy_nonoverlapping(key, out, 32);
        asm!(
            "ecall",
            in("a7") Ecall::EthereumLoad as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn storage_cache_bytes32(key: *const u8, value: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::EthereumStore as usize,
            in("a0") key,
            in("a1") value,
            options(nostack)
        );
    }
}

pub unsafe fn transient_load_bytes32(key: *const u8, out: *mut u8) {
    unsafe {
        core::ptr::copy_nonoverlapping(key, out, 32);
        asm!(
            "ecall",
            in("a7") Ecall::TransientLoad as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn transient_store_bytes32(key: *const u8, value: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::TransientStore as usize,
            in("a0") key,
            in("a1") value,
            options(nostack)
        );
    }
}

pub unsafe fn storage_flush_cache(clear: bool) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::StorageFlushCache as usize,
            in("a1") clear as usize,
            options(nostack)
        );
    }
}

pub unsafe fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8) {
    unsafe {
        core::ptr::copy_nonoverlapping(bytes, output, 32);
        asm!(
            "ecall",
            in("a7") Ecall::NativeKeccak256 as usize,
            in("a0") output,
            in("a1") len,
            options(nostack)
        );
    }
}

pub unsafe fn emit_log(data: *const u8, len: usize, topics: usize) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::EmitLog as usize,
            in("a0") data,
            in("a1") len,
            in("a2") topics,
            options(nostack)
        );
    }
}

pub unsafe fn account_balance(addr: *const u8, dest: *mut u8) {
    unsafe {
        core::ptr::copy_nonoverlapping(addr, dest, 20);
        asm!(
            "ecall",
            in("a7") Ecall::AccountBalance as usize,
            in("a0") dest,
            options(nostack)
        );
    }
}

pub unsafe fn account_code(address: *const u8, offset: usize, size: usize, dest: *mut u8) -> usize {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::AccountCode as usize,
            in("a0") address,
            in("a1") offset,
            in("a2") size,
            in("a3") dest,
            options(nostack)
        );
    }
    size
}

pub unsafe fn account_code_size(address: *const u8) -> usize {
    let ret: usize;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::AccountCodeSize as usize,
            inout("a0") address => ret,
            options(nostack)
        );
    }
    ret
}

pub unsafe fn account_codehash(address: *const u8, dest: *mut u8) {
    unsafe {
        core::ptr::copy_nonoverlapping(address, dest, 20);
        asm!(
            "ecall",
            in("a7") Ecall::AccountCodehash as usize,
            in("a0") dest,
            options(nostack)
        );
    }
}

pub unsafe fn block_basefee(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::BlockBasefee as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn chainid() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::Chainid as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn block_coinbase(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::BlockCoinbase as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn block_gas_limit() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::BlockGasLimit as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn block_number() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::BlockNumber as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn block_timestamp() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::BlockTimestamp as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn call_contract(
    contract: *const u8,
    calldata: *const u8,
    calldata_len: usize,
    value: *const u8,
    gas: u64,
    return_data_len: *mut usize,
) -> u8 {
    let ret: usize;
    let gas_hi = (gas >> 32) as u32;
    let gas_lo = gas as u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::CallContract as usize,
            inout("a0") contract as usize => ret,
            in("a1") calldata,
            in("a2") calldata_len,
            in("a3") value,
            in("a4") gas_hi,
            in("a5") gas_lo,
            in("a6") return_data_len,
            options(nostack)
        );
    }
    ret as u8
}

pub unsafe fn static_call_contract(
    contract: *const u8,
    calldata: *const u8,
    calldata_len: usize,
    gas: u64,
    return_data_len: *mut usize,
) -> u8 {
    let ret: usize;
    let gas_hi = (gas >> 32) as u32;
    let gas_lo = gas as u32;
    let dummy_value: [u8; 32] = [0; 32];
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::StaticCallContract as usize,
            inout("a0") contract as usize => ret,
            in("a1") calldata,
            in("a2") calldata_len,
            in("a3") dummy_value.as_ptr(),
            in("a4") gas_hi,
            in("a5") gas_lo,
            in("a6") return_data_len,
            options(nostack)
        );
    }
    ret as u8
}

pub unsafe fn delegate_call_contract(
    contract: *const u8,
    calldata: *const u8,
    calldata_len: usize,
    gas: u64,
    return_data_len: *mut usize,
) -> u8 {
    let ret: usize;
    let gas_hi = (gas >> 32) as u32;
    let gas_lo = gas as u32;
    let dummy_value: [u8; 32] = [0; 32];
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::DelegateCallContract as usize,
            inout("a0") contract as usize => ret,
            in("a1") calldata,
            in("a2") calldata_len,
            in("a3") dummy_value.as_ptr(),
            in("a4") gas_hi,
            in("a5") gas_lo,
            in("a6") return_data_len,
            options(nostack)
        );
    }
    ret as u8
}

pub unsafe fn contract_address(addr: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ContractAddress as usize,
            in("a0") addr,
            options(nostack)
        );
    }
}

pub unsafe fn create1(
    code: *const u8,
    code_len: usize,
    endowment: *const u8,
    contract: *mut u8,
    revert_data_len: *mut usize,
) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::Create1 as usize,
            in("a0") contract,
            in("a1") code_len,
            in("a2") endowment,
            in("a3") code,
            in("a4") revert_data_len,
            options(nostack)
        );
    }
}

pub unsafe fn create2(
    code: *const u8,
    code_len: usize,
    endowment: *const u8,
    salt: *const u8,
    contract: *mut u8,
    revert_data_len: *mut usize,
) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::Create2 as usize,
            in("a0") contract,
            in("a1") code_len,
            in("a2") endowment,
            in("a3") salt,
            in("a4") code,
            in("a5") revert_data_len,
            options(nostack)
        );
    }
}

pub unsafe fn evm_gas_left() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::EvmGasLeft as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn evm_ink_left() -> u64 {
    let hi: u32;
    let lo: u32;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::EvmInkLeft as usize,
            lateout("a0") hi,
            lateout("a1") lo,
            options(nostack)
        );
    }
    ((hi as u64) << 32) | (lo as u64)
}

pub unsafe fn pay_for_memory_grow(pages: u16) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::PayForMemoryGrow as usize,
            in("a0") pages as usize,
            options(nostack)
        );
    }
}

pub unsafe fn msg_reentrant() -> bool {
    let ret: usize;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MsgReentrant as usize,
            lateout("a0") ret,
            options(nostack)
        );
    }
    ret != 0
}

pub unsafe fn msg_sender(addr: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MsgSender as usize,
            in("a0") addr,
            options(nostack)
        );
    }
}

pub unsafe fn msg_value(value: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MsgValue as usize,
            in("a0") value,
            options(nostack)
        );
    }
}

pub unsafe fn read_args(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ReadArgs as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn args_len() -> usize {
    let ret: usize;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ArgsLen as usize,
            lateout("a0") ret,
            options(nostack)
        );
    }
    ret
}

pub unsafe fn read_return_data(dest: *mut u8, offset: usize, size: usize) -> usize {
    let ret: usize;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ReadReturnData as usize,
            inout("a0") dest => ret,
            in("a1") offset,
            in("a2") size,
            options(nostack)
        );
    }
    ret
}

pub unsafe fn return_data_size() -> usize {
    let ret: usize;
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ReturnDataSize as usize,
            lateout("a0") ret,
            options(nostack)
        );
    }
    ret
}

pub unsafe fn tx_gas_price(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::TxGasPrice as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn tx_ink_price(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::TxInkPrice as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn tx_origin(out: *mut u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::TxOrigin as usize,
            in("a0") out,
            options(nostack)
        );
    }
}

pub unsafe fn math_div(x: *mut u8, y: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MathDiv as usize,
            in("a0") x,
            in("a1") y,
            options(nostack)
        );
    }
}

pub unsafe fn math_mod(x: *mut u8, y: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MathMod as usize,
            in("a0") x,
            in("a1") y,
            options(nostack)
        );
    }
}

pub unsafe fn math_add_mod(x: *mut u8, y: *const u8, z: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MathAddMod as usize,
            in("a0") x,
            in("a1") y,
            in("a2") z,
            options(nostack)
        );
    }
}

pub unsafe fn math_mul_mod(x: *mut u8, y: *const u8, z: *const u8) {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::MathMulMod as usize,
            in("a0") x,
            in("a1") y,
            in("a2") z,
            options(nostack)
        );
    }
}

pub unsafe fn exit_early(code: i32) -> ! {
    unsafe {
        asm!(
            "ecall",
            in("a7") Ecall::ExitEarly as usize,
            in("a0") code,
            options(nostack)
        );
    }
    loop {}
}
