#![allow(clippy::explicit_auto_deref)]
use crate::stdlib::prelude::*;
use thiserror_no_std::Error;
use crate::{
    types::{
        errors::math_errors::MathError,
        relocatable::{MaybeRelocatable, Relocatable},
    },
    vm::errors::{
        exec_scope_errors::ExecScopeError, hint_errors::HintError, memory_errors::MemoryError,
        runner_errors::RunnerError, trace_errors::TraceError,
    },
};
use felt::Felt252;
pub const HINT_ERROR_STR: &str = "Got an exception while executing a hint: ";
#[derive(Debug, Error)]
pub enum VirtualMachineError {
    #[error(transparent)]
    RunnerError(#[from] RunnerError),
    #[error(transparent)]
    Memory(#[from] MemoryError),
    #[error(transparent)]
    Math(#[from] MathError),
    #[error(transparent)]
    TracerError(#[from] TraceError),
    #[error(transparent)]
    MainScopeError(#[from] ExecScopeError),
    #[error(transparent)]
    Other(anyhow::Error),
    #[error("Instruction MSB should be 0")]
    InstructionNonZeroHighBit,
    #[error("Instruction should be an int")]
    InvalidInstructionEncoding,
    #[error("Invalid op1_register value: {0}")]
    InvalidOp1Reg(u64),
    #[error("In immediate mode, off2 should be 1")]
    ImmShouldBe1,
    #[error("op0 must be known in double dereference")]
    UnknownOp0,
    #[error("Invalid ap_update value: {0}")]
    InvalidApUpdate(u64),
    #[error("Invalid pc_update value: {0}")]
    InvalidPcUpdate(u64),
    #[error("Res.UNCONSTRAINED cannot be used with ApUpdate.ADD")]
    UnconstrainedResAdd,
    #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP")]
    UnconstrainedResJump,
    #[error("Res.UNCONSTRAINED cannot be used with PcUpdate.JUMP_REL")]
    UnconstrainedResJumpRel,
    #[error("Res.UNCONSTRAINED cannot be used with Opcode.ASSERT_EQ")]
    UnconstrainedResAssertEq,
    #[error("A relocatable value as Res cannot be used with PcUpdate.JUMP_REL")]
    JumpRelNotInt,
    #[error(
        "Failed to compute Res.MUL: Could not complete computation of non pure values {} * {}", (*.0).0, (*.0).1
    )]
    ComputeResRelocatableMul(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Couldn't compute operand {}. Unknown value for memory cell {}", (*.0).0, (*.0).1)]
    FailedToComputeOperands(Box<(String, Relocatable)>),
    #[error("An ASSERT_EQ instruction failed: {} != {}.", (*.0).0, (*.0).1)]
    DiffAssertValues(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Call failed to write return-pc (inconsistent op0): {} != {}. Did you forget to increment ap?", (*.0).0, (*.0).1)]
    CantWriteReturnPc(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Call failed to write return-fp (inconsistent dst): {} != {}. Did you forget to increment ap?", (*.0).0, (*.0).1)]
    CantWriteReturnFp(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Couldn't get or load dst")]
    NoDst,
    #[error("Invalid res value: {0}")]
    InvalidRes(u64),
    #[error("Invalid opcode value: {0}")]
    InvalidOpcode(u64),
    #[error("This is not implemented")]
    NotImplemented,
    #[error("Inconsistent auto-deduction for builtin {}, expected {}, got {:?}", (*.0).0, (*.0).1, (*.0).2)]
    InconsistentAutoDeduction(Box<(&'static str, MaybeRelocatable, Option<MaybeRelocatable>)>),
    #[error("Invalid hint encoding at pc: {0}")]
    InvalidHintEncoding(Box<MaybeRelocatable>),
    #[error("Expected range_check builtin to be present")]
    NoRangeCheckBuiltin,
    #[error("Expected ecdsa builtin to be present")]
    NoSignatureBuiltin,
    #[error("Div out of range: 0 < {} <= {}", (*.0).0, (*.0).1)]
    OutOfValidRange(Box<(Felt252, Felt252)>),
    #[error("Failed to compare {} and {}, cant compare a relocatable to an integer value", (*.0).0, (*.0).1)]
    DiffTypeComparison(Box<(MaybeRelocatable, MaybeRelocatable)>),
    #[error("Failed to compare {} and  {}, cant compare two relocatable values of different segment indexes", (*.0).0, (*.0).1)]
    DiffIndexComp(Box<(Relocatable, Relocatable)>),
    #[error("Couldn't convert usize to u32")]
    NoneInMemoryRange,
    #[error("Expected integer, found: {0:?}")]
    ExpectedIntAtRange(Box<Option<MaybeRelocatable>>),
    #[error("Could not convert slice to array")]
    SliceToArrayError,
    #[error("Failed to compile hint: {0}")]
    CompileHintFail(Box<str>),
    #[error("op1_addr is Op1Addr.IMM, but no immediate was given")]
    NoImm,
    #[error("Execution reached the end of the program. Requested remaining steps: {0}.")]
    EndOfProgram(usize),
    #[error("Could not reach the end of the program. Executed steps: {0}.")]
    StepsLimit(u64),
    #[error("Could not reach the end of the program. RunResources has no remaining steps.")]
    UnfinishedExecution,
    #[error("Current run is not finished")]
    RunNotFinished,
    #[error("Invalid argument count, expected {} but got {}", (*.0).0, (*.0).1)]
    InvalidArgCount(Box<(usize, usize)>),
    #[error("Couldn't parse prime: {0}")]
    CouldntParsePrime(Box<str>),
    #[error("{HINT_ERROR_STR}{}", (*.0).1)]
    Hint(Box<(usize, HintError)>),
    #[error("Unexpected Failure")]
    Unexpected,
    #[error("Out of bounds access to builtin segment")]
    OutOfBoundsBuiltinSegmentAccess,
    #[error("Out of bounds access to program segment")]
    OutOfBoundsProgramSegmentAccess,
    #[error("Security Error: Invalid Memory Value: temporary address not relocated: {0}")]
    InvalidMemoryValueTemporaryAddress(Box<Relocatable>),
    #[error("accessed_addresses is None.")]
    MissingAccessedAddresses,
    #[error("Failed to write the output builtin content")]
    FailedToWriteOutput,
    #[error("Failed to find index {0} in the vm's relocation table")]
    RelocationNotFound(usize),
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_vm_error_size() {
        let size = crate::stdlib::mem::size_of::<VirtualMachineError>();
        assert!(size <= 32, "{size}")
    }
}