//! JVM bytecode interpreter.
//!
//! This module provides the core JVM bytecode execution engine for JVMRS.
//! It implements a stack-based interpreter with JIT compilation support,
//! inline caching for method dispatch, and comprehensive debugging capabilities.
//!
//! # Architecture
//!
//! The interpreter is organized into submodules:
//! - `descriptor` - Method descriptor parsing and validation
//! - `utils` - Bytecode reading helpers and utilities
//! - `invocation` - Method invocation (invokevirtual, invokestatic, execute_method)
//! - `dispatch` - Instruction dispatch and opcode handling
//! - `builtins` - Native builtins (println, invokedynamic)
//!
//! # Performance Features
//!
//! - **Inline Caching**: Optimized virtual method dispatch with per-call-site caching
//! - **Tiered Compilation**: Automatic JIT compilation of hot methods
//! - **Profile-Guided Optimization**: Adaptive optimization based on runtime behavior
//!
//! # Example
//!
//! ```rust
//! use jvmrs::Interpreter;
//!
//! // Create a new interpreter
//! # let mut interpreter = Interpreter::new();
//! // Load and execute a Java class
//! # // interpreter.run_main("com/example/HelloWorld")?;
//! # Ok::<(), jvmrs::JvmError>(())
//! ```

pub mod descriptor;
pub mod traits;
mod utils;

mod builtins;
mod dispatch;
mod dispatch_table;
mod invocation;

pub(crate) use dispatch_table::DISPATCH_TABLE;

use crate::inline_cache::InlineCacheManager;  // InlineCacheEntry temporarily unused

use crate::class_file::{AttributeInfo, ClassFile, MethodInfo};
use crate::class_loader::ClassLoader;
use crate::debug::{debug_config_from_env, JvmDebugger};
use crate::deterministic::DeterministicConfig;
use crate::error::{ClassLoadingError, JvmError, RuntimeError};
use crate::jit::{JitManager, TieredCompilationConfig};
use crate::memory::{Memory, StackFrame, Value};
use crate::native::{init_builtins, NativeRegistry};
use crate::profiler::Profiler;
use crate::reflection::ReflectionApi;
use crate::security::Sanitizer;
use crate::trace::TraceRecorder;

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

/// Result type for interpreter operations
///
/// This is a type alias for `Result<(), JvmError>` used throughout
/// the interpreter to indicate success or failure of operations.
pub type InterpreterResult = Result<(), JvmError>;

/// JVM bytecode interpreter
///
/// The `Interpreter` is the core execution engine of JVMRS. It manages:
/// - Class loading and resolution
/// - Memory management (heap and stack)
/// - Bytecode execution and JIT compilation
/// - Method invocation and inline caching
/// - Native method dispatch
/// - Reflection and debugging capabilities
///
/// # Features
///
/// - **JIT Compilation**: Automatic tiered compilation with Cranelift backend
/// - **Inline Caching**: Optimized virtual method dispatch
/// - **Memory Safety**: Rust-based implementation eliminates entire classes of bugs
/// - **Polyglot Support**: Seamless Java/Rust interoperability
/// - **Debugging**: Integrated profiler, trace recording, and deterministic execution
///
/// # Thread Safety
///
/// The interpreter is not thread-safe by design. For multi-threaded execution,
/// create a separate interpreter instance per thread or use the thread-safe
/// variants provided by the API.
///
/// # Example
///
/// ```rust
/// use jvmrs::Interpreter;
///
/// // Create interpreter with default classpath
/// # let mut interpreter = Interpreter::new();
/// // Run main method
/// # // interpreter.run_main("com/example/Application")?;
/// # Ok::<(), jvmrs::JvmError>(())
/// ```
pub struct Interpreter {
    pub(crate) class_loader: ClassLoader,
    pub(crate) memory: Memory,
    pub(crate) string_cache: HashMap<u32, String>,
    pub(crate) exception_handlers: Vec<ExceptionHandler>,
    pub(crate) current_exception: Option<RuntimeError>,
    pub(crate) debugger: JvmDebugger,
    pub(crate) current_thread_id: u32,
    pub(crate) native_registry: NativeRegistry,
    pub(crate) reflection_api: ReflectionApi,
    pub(crate) jit_manager: Option<JitManager>,
    pub(crate) jit_config: TieredCompilationConfig,
    pub(crate) profiler: Option<Arc<Profiler>>,
    pub(crate) trace_recorder: Option<TraceRecorder>,
    pub(crate) sanitizer: Option<Arc<Sanitizer>>,
    pub(crate) deterministic_config: Option<DeterministicConfig>,
    pub(crate) inline_cache_manager: InlineCacheManager,
}

/// Exception handler information
#[derive(Debug, Clone)]
pub(crate) struct ExceptionHandler {
    pub start_pc: usize,
    pub end_pc: usize,
    pub handler_pc: usize,
    pub catch_type: Option<String>,
}

impl Interpreter {
    /// Create a new interpreter with default classpath
    pub fn new() -> Self {
        let debug_config = debug_config_from_env();
        let debugger = JvmDebugger::new(debug_config);
        let memory = Memory::with_debugger(debugger.clone());
        let mut native_registry = NativeRegistry::new();
        init_builtins(&mut native_registry);
        let reflection_api = ReflectionApi::new();

        let jit_manager = JitManager::new().ok();
        let jit_config = TieredCompilationConfig::default();

        Interpreter {
            class_loader: ClassLoader::new_default(),
            memory,
            string_cache: HashMap::new(),
            exception_handlers: Vec::new(),
            current_exception: None,
            debugger,
            current_thread_id: 1,
            native_registry,
            reflection_api,
            jit_manager,
            jit_config,
            profiler: None,
            trace_recorder: None,
            sanitizer: None,
            deterministic_config: None,
            inline_cache_manager: InlineCacheManager::new(true),
        }
    }

    /// Create a new interpreter with custom classpath
    pub fn with_classpath(classpath: Vec<PathBuf>) -> Self {
        let debug_config = debug_config_from_env();
        let debugger = JvmDebugger::new(debug_config);
        let memory = Memory::with_debugger(debugger.clone());
        let mut native_registry = NativeRegistry::new();
        init_builtins(&mut native_registry);
        let reflection_api = ReflectionApi::new();

        let jit_manager = JitManager::new().ok();
        let jit_config = TieredCompilationConfig::default();

        Interpreter {
            class_loader: ClassLoader::new(classpath),
            memory,
            string_cache: HashMap::new(),
            exception_handlers: Vec::new(),
            current_exception: None,
            debugger,
            current_thread_id: 1,
            native_registry,
            reflection_api,
            jit_manager,
            jit_config,
            profiler: None,
            trace_recorder: None,
            sanitizer: None,
            deterministic_config: None,
            inline_cache_manager: InlineCacheManager::new(true),
        }
    }

    /// Check if object class is assignable to target class (instanceof check)
    pub fn is_assignable_from(&mut self, obj_class: &str, target_class: &str) -> bool {
        if obj_class == target_class {
            return true;
        }
        if target_class == "java/lang/Object" {
            return true;
        }

        // Handle array types
        if obj_class.starts_with('[') {
            if target_class == "java/lang/Cloneable" || target_class == "java/io/Serializable" {
                return true;
            }
            if target_class.starts_with('[') {
                // Both arrays: check component types
                if obj_class.len() > 1 && target_class.len() > 1 {
                    let obj_comp = &obj_class[1..];
                    let target_comp = &target_class[1..];
                    // If component is primitive, must match exactly (handled by first check)
                    // If object arrays, check component assignability
                    if obj_comp.starts_with('L') && target_comp.starts_with('L') {
                        let obj_inner = &obj_comp[1..obj_comp.len() - 1]; // Strip L and ;
                        let target_inner = &target_comp[1..target_comp.len() - 1];
                        return self.is_assignable_from(obj_inner, target_inner);
                    }
                    if obj_comp.starts_with('[') && target_comp.starts_with('[') {
                        return self.is_assignable_from(obj_comp, target_comp);
                    }
                }
            }
            return false;
        }

        // If primitive type (should not happen for reference check, but safety)
        if obj_class.len() == 1 {
            return false;
        }

        // Load target class to ensure it exists (required for resolution)
        if !self.class_loader.is_class_loaded(target_class) {
            let _ = self.class_loader.load_class(target_class);
        }

        // Check hierarchy
        self.check_hierarchy(obj_class, target_class, 0)
    }

    fn check_hierarchy(&self, current: &str, target: &str, depth: usize) -> bool {
        if depth > 100 {
            return false;
        } // Prevent infinite recursion
        if current == target {
            return true;
        }

        let class = match self.class_loader.get_class(current) {
            Some(c) => c,
            None => return false,
        };

        // Check superclass
        if let Some(super_name) = class.get_super_class_name() {
            if self.check_hierarchy(&super_name, target, depth + 1) {
                return true;
            }
        }

        // Check interfaces
        for &idx in &class.interfaces {
            if let Some(interface_name) = class.get_class_name_from_index(idx) {
                if self.check_hierarchy(&interface_name, target, depth + 1) {
                    return true;
                }
            }
        }

        false
    }

    /// Allocate multi-dimensional array
    pub fn allocate_multi_array(
        &mut self,
        type_desc: &str,
        counts: &[i32],
    ) -> Result<u32, RuntimeError> {
        if counts.is_empty() {
            return Err(RuntimeError::IllegalArgument("Counts empty".to_string()));
        }
        let count = counts[0];
        if count < 0 {
            return Err(RuntimeError::NegativeArraySizeException(count));
        }

        use crate::memory::HeapArray;

        if counts.len() == 1 {
            // Create leaf array for this recursion
            // Check component type
            let component = &type_desc[1..];
            if component.starts_with('L') || component.starts_with('[') {
                let arr = HeapArray::ReferenceArray(type_desc.to_string(), vec![0; count as usize]); // 0 is null
                return Ok(self.memory.heap.allocate_array(arr));
            }

            // Primitive
            let arr = match component.chars().next().unwrap() {
                'Z' => HeapArray::BooleanArray(vec![false; count as usize]),
                'C' => HeapArray::CharArray(vec![0u16; count as usize]),
                'F' => HeapArray::FloatArray(vec![0.0; count as usize]),
                'D' => HeapArray::DoubleArray(vec![0.0; count as usize]),
                'B' => HeapArray::ByteArray(vec![0; count as usize]),
                'S' => HeapArray::ShortArray(vec![0; count as usize]),
                'I' => HeapArray::IntArray(vec![0; count as usize]),
                'J' => HeapArray::LongArray(vec![0; count as usize]),
                _ => {
                    return Err(RuntimeError::IllegalArgument(format!(
                        "Unknown array type: {}",
                        component
                    )))
                }
            };
            return Ok(self.memory.heap.allocate_array(arr));
        }

        // Recursive case: counts.len() > 1
        // Must be reference array (array of arrays)
        let arr_ref = self.memory.heap.allocate_array(HeapArray::ReferenceArray(
            type_desc.to_string(),
            vec![0; count as usize],
        ));

        let component_type = &type_desc[1..];
        for i in 0..count {
            let val = self.allocate_multi_array(component_type, &counts[1..])?;
            self.memory
                .heap
                .array_set(arr_ref, i as usize, Value::ArrayRef(val))
                .map_err(RuntimeError::from)?;
        }

        Ok(arr_ref)
    }

    /// Get the class name of a value (object or array)
    pub fn get_value_class(&self, val: &Value) -> Result<String, RuntimeError> {
        match val {
            Value::Null => Ok("null".to_string()),
            Value::Reference(addr) => {
                let obj = self
                    .memory
                    .heap
                    .get_object(*addr)
                    .ok_or(RuntimeError::InvalidReference(*addr))?;
                Ok(obj.class_name.clone())
            }
            Value::ArrayRef(addr) => {
                let arr = self
                    .memory
                    .heap
                    .get_array(*addr)
                    .ok_or(RuntimeError::InvalidReference(*addr))?;
                Ok(self.get_array_class(arr))
            }
            _ => Err(RuntimeError::ClassCastException(
                "Not an object".to_string(),
                "Object".to_string(),
            )),
        }
    }

    fn get_array_class(&self, arr: &crate::memory::HeapArray) -> String {
        use crate::memory::HeapArray;
        match arr {
            HeapArray::BooleanArray(_) => "[Z".to_string(),
            HeapArray::CharArray(_) => "[C".to_string(),
            HeapArray::FloatArray(_) => "[F".to_string(),
            HeapArray::DoubleArray(_) => "[D".to_string(),
            HeapArray::ByteArray(_) => "[B".to_string(),
            HeapArray::ShortArray(_) => "[S".to_string(),
            HeapArray::IntArray(_) => "[I".to_string(),
            HeapArray::LongArray(_) => "[J".to_string(),
            HeapArray::ReferenceArray(class_name, _) => class_name.clone(),
        }
    }

    pub fn value_to_string(&self, val: &Value) -> String {
        match val {
            Value::Null => "null".to_string(),
            Value::Boolean(b) => b.to_string(),
            Value::Byte(b) => b.to_string(),
            Value::Char(c) => char::from_u32(*c as u32)
                .map(|c| c.to_string())
                .unwrap_or_else(|| format!("\\u{:04x}", c)),
            Value::Short(s) => s.to_string(),
            Value::Int(i) => i.to_string(),
            Value::Long(l) => l.to_string(),
            Value::Float(f) => f.to_string(),
            Value::Double(d) => d.to_string(),
            Value::Reference(addr) => {
                if let Some(obj) = self.memory.heap.get_object(*addr) {
                    if obj.class_name == "java/lang/String" {
                        if let Some(s) = &obj.string_data {
                            return s.clone();
                        }
                        if let Some(Value::Reference(s_ref)) = obj.fields.get("value") {
                            if let Some(arr) = self.memory.heap.get_array(*s_ref) {
                                if let crate::memory::HeapArray::ByteArray(bytes) = arr {
                                    return String::from_utf8_lossy(bytes).to_string();
                                }
                            }
                        }
                    }
                    format!("<{}@{:x}>", obj.class_name, addr)
                } else {
                    format!("<invalid reference @{}>", addr)
                }
            }
            Value::ArrayRef(addr) => {
                if let Some(arr) = self.memory.heap.get_array(*addr) {
                    match arr {
                        crate::memory::HeapArray::BooleanArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::CharArray(data) => {
                            let s: String = data.iter().map(|&c| (c as u8) as char).collect();
                            format!("{:?}", s)
                        }
                        crate::memory::HeapArray::FloatArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::DoubleArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::ByteArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::ShortArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::IntArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::LongArray(data) => format!("{:?}", data),
                        crate::memory::HeapArray::ReferenceArray(_, data) => {
                            format!("[... {} elements]", data.len())
                        }
                    }
                } else {
                    format!("<invalid array ref @{}>", addr)
                }
            }
            Value::ReturnAddress(addr) => format!("<return address @{}>", addr),
        }
    }

    /// Create a new interpreter with JIT enabled and custom config
    pub fn with_jit(jit_config: TieredCompilationConfig) -> Self {
        let debug_config = debug_config_from_env();
        let debugger = JvmDebugger::new(debug_config);
        let memory = Memory::with_debugger(debugger.clone());
        let mut native_registry = NativeRegistry::new();
        init_builtins(&mut native_registry);
        let reflection_api = ReflectionApi::new();

        let jit_manager = JitManager::with_config(jit_config.clone()).ok();

        Interpreter {
            class_loader: ClassLoader::new_default(),
            memory,
            string_cache: HashMap::new(),
            exception_handlers: Vec::new(),
            current_exception: None,
            debugger,
            current_thread_id: 1,
            native_registry,
            reflection_api,
            jit_manager,
            jit_config,
            profiler: None,
            trace_recorder: None,
            inline_cache_manager: InlineCacheManager::new(true),
            sanitizer: None,
            deterministic_config: None,
        }
    }

    /// Enable deterministic execution
    pub fn set_deterministic(&mut self, config: Option<DeterministicConfig>) {
        self.deterministic_config = config;
    }

    /// Enable profiling
    pub fn set_profiler(&mut self, profiler: Option<Arc<Profiler>>) {
        self.profiler = profiler;
    }

    /// Get profiler reference
    pub fn profiler(&self) -> Option<&Arc<Profiler>> {
        self.profiler.as_ref()
    }

    /// Enable trace recording
    pub fn set_trace_recorder(&mut self, mut recorder: Option<TraceRecorder>) {
        if let Some(ref mut r) = recorder {
            r.set_enabled(true);
        }
        self.trace_recorder = recorder;
    }

    /// Enable security sanitizer
    pub fn set_sanitizer(&mut self, sanitizer: Option<Arc<Sanitizer>>) {
        self.memory.set_sanitizer(sanitizer.clone());
        self.sanitizer = sanitizer;
    }

    /// Set class cache directory
    pub fn set_class_cache_dir(&mut self, path: Option<PathBuf>) {
        self.class_loader.set_cache_dir(path);
    }

    /// Get trace recorder reference
    /// Get reference to memory for visualization/debugging
    pub fn memory(&self) -> &Memory {
        &self.memory
    }

    /// Get mutable reference to memory (for reflection and allocation)
    pub fn memory_mut(&mut self) -> &mut Memory {
        &mut self.memory
    }

    /// Create a new instance of a class using reflection.
    /// Allocates on the heap and optionally invokes the default constructor.
    pub fn new_instance(&mut self, class_name: &str, args: &[Value]) -> Result<Value, JvmError> {
        self.class_loader.load_class(class_name)?;
        let class = self
            .class_loader
            .get_class(class_name)
            .ok_or_else(|| {
                JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(
                    class_name.to_string(),
                ))
            })?
            .clone();
        let internal_name = class
            .get_class_name()
            .unwrap_or_else(|| class_name.to_string());
        let addr = self.memory.heap.allocate(internal_name);
        if args.is_empty() {
            if let Some(init) = class.find_method("<init>", "()V") {
                let _ = self.invoke_constructor_for_reflection(&class, init, addr);
            }
        }
        Ok(Value::Reference(addr))
    }

    /// Get the value of a field from an object
    pub fn get_field_value(&self, obj: &Value, field_name: &str) -> Result<Value, JvmError> {
        let addr = obj.as_reference().ok_or_else(|| {
            JvmError::RuntimeError(RuntimeError::IllegalArgument(
                "Not an object reference".to_string(),
            ))
        })?;
        self.memory.heap.get_field(addr, field_name).ok_or_else(|| {
            JvmError::RuntimeError(RuntimeError::IllegalArgument(format!(
                "Field '{}' not found or not initialized",
                field_name
            )))
        })
    }

    /// Set the value of a field in an object
    pub fn set_field_value(
        &mut self,
        obj: &Value,
        field_name: &str,
        value: Value,
    ) -> Result<(), JvmError> {
        let addr = obj.as_reference().ok_or_else(|| {
            JvmError::RuntimeError(RuntimeError::IllegalArgument(
                "Not an object reference".to_string(),
            ))
        })?;
        self.memory
            .heap
            .set_field(addr, field_name.to_string(), value)
            .map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))
    }

    /// Get the class name of an object
    pub fn get_object_class(&self, obj: &Value) -> Result<String, JvmError> {
        let addr = obj.as_reference().ok_or_else(|| {
            JvmError::RuntimeError(RuntimeError::IllegalArgument(
                "Not an object reference".to_string(),
            ))
        })?;
        self.memory
            .heap
            .get_object(addr)
            .map(|o| o.class_name.clone())
            .ok_or_else(|| {
                JvmError::RuntimeError(RuntimeError::IllegalArgument(format!(
                    "Invalid object reference {}",
                    addr
                )))
            })
    }

    /// Invoke a method on an object using reflection
    pub fn invoke_method(
        &mut self,
        obj: &Value,
        method_name: &str,
        args: &[Value],
    ) -> Result<Value, JvmError> {
        let addr = obj.as_reference().ok_or_else(|| {
            JvmError::RuntimeError(RuntimeError::IllegalArgument(
                "Not an object reference".to_string(),
            ))
        })?;
        let class_name = self.get_object_class(obj)?;
        let class = self
            .class_loader
            .get_class(&class_name)
            .ok_or_else(|| {
                JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(
                    class_name.clone(),
                ))
            })?
            .clone();
        let descriptor = Self::build_descriptor_for_args(args);
        let method = class
            .find_method(method_name, &descriptor)
            .or_else(|| class.find_method(method_name, "()I"))
            .or_else(|| class.find_method(method_name, "()V"))
            .ok_or_else(|| {
                JvmError::RuntimeError(RuntimeError::MethodNotFound(
                    class_name.clone(),
                    method_name.to_string(),
                ))
            })?;
        let desc = class
            .get_string(method.descriptor_index)
            .unwrap_or_default();
        let mut caller_frame = StackFrame::new(0, 64, "reflection_caller".to_string());
        for v in args.iter().rev() {
            caller_frame.push(v.clone()).map_err(|e| {
                JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string()))
            })?;
        }
        caller_frame
            .push(Value::Reference(addr))
            .map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))?;
        self.execute_method(&class, method, &mut caller_frame)?;
        if desc.ends_with(")V") {
            Ok(Value::Int(0))
        } else {
            caller_frame
                .pop()
                .map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))
        }
    }

    fn build_descriptor_for_args(args: &[Value]) -> String {
        let mut s = String::from("(");
        for v in args {
            match v {
                Value::Int(_) => s.push('I'),
                Value::Long(_) => s.push('J'),
                Value::Float(_) => s.push('F'),
                Value::Double(_) => s.push('D'),
                Value::Reference(_) | Value::ArrayRef(_) => s.push_str("Ljava/lang/Object;"),
                _ => s.push('I'),
            }
        }
        s.push(')');
        s.push('I');
        s
    }

    fn invoke_constructor_for_reflection(
        &mut self,
        class: &ClassFile,
        method: &MethodInfo,
        addr: u32,
    ) -> InterpreterResult {
        let mut caller_frame = StackFrame::new(0, 64, "reflection_caller".to_string());
        caller_frame.push(Value::Reference(addr))?;
        self.execute_method(class, method, &mut caller_frame)
    }

    pub fn trace_recorder(&self) -> Option<&TraceRecorder> {
        self.trace_recorder.as_ref()
    }

    /// Get the number of loaded classes
    pub fn get_loaded_class_count(&self) -> usize {
        self.class_loader.get_loaded_classes().len()
    }

    /// Get current heap memory usage in bytes
    pub fn get_memory_usage(&self) -> usize {
        self.memory.heap.memory_used()
    }

    /// Get the number of GC runs performed
    pub fn get_gc_count(&self) -> usize {
        // Tracked via memory stats; return object count as proxy
        self.memory.heap.object_count()
    }

    /// Check if JIT is enabled
    pub fn is_jit_enabled(&self) -> bool {
        self.jit_manager.is_some() && self.jit_config.enabled
    }

    /// Enable or disable JIT compilation
    pub fn set_jit_enabled(&mut self, enabled: bool) {
        if enabled && self.jit_manager.is_none() {
            self.jit_manager = JitManager::new().ok();
        } else if !enabled {
            self.jit_manager = None;
        }
        self.jit_config.enabled = enabled;
    }

    /// Get the JIT manager
    pub fn jit_manager(&mut self) -> Option<&mut JitManager> {
        self.jit_manager.as_mut()
    }

    /// Check if a class is loaded
    pub fn is_class_loaded(&self, name: &str) -> bool {
        self.class_loader.is_class_loaded(name)
    }

    /// Load a class from a file (legacy)
    pub fn load_class<P: AsRef<Path>>(&mut self, path: P) -> Result<(), JvmError> {
        let _ = ClassFile::from_file(path).map_err(|e| {
            JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(format!(
                "Failed to load class: {:?}",
                e
            )))
        })?;
        Ok(())
    }

    /// Load a class by name using classpath resolution
    pub fn load_class_by_name(&mut self, class_name: &str) -> Result<(), JvmError> {
        self.class_loader.load_class(class_name)?;
        Ok(())
    }

    /// Get a loaded class by name
    pub fn get_class(&self, name: &str) -> Option<&ClassFile> {
        self.class_loader.get_class(name)
    }

    /// Get the reflection API instance
    pub fn get_reflection_api(&self) -> &ReflectionApi {
        &self.reflection_api
    }

    /// Get reflection information for a loaded class
    pub fn get_class_reflection(
        &self,
        class_name: &str,
    ) -> Option<crate::reflection::ClassReflection> {
        let class = self.class_loader.get_class(class_name)?;
        Some(crate::reflection::class_to_reflection(class))
    }

    /// Run the main method of a class
    pub fn run_main(&mut self, class_name: &str) -> Result<(), JvmError> {
        self.load_class_by_name(class_name)?;

        let class = self.class_loader.get_class(class_name).ok_or_else(|| {
            JvmError::ClassLoadingError(ClassLoadingError::NoClassDefFound(class_name.to_string()))
        })?;

        let main_method = class
            .find_method("main", "([Ljava/lang/String;)V")
            .ok_or_else(|| {
                JvmError::RuntimeError(RuntimeError::MethodNotFound(
                    class_name.to_string(),
                    "main([Ljava/lang/String;)V".to_string(),
                ))
            })?
            .clone();

        let code_attr = self
            .find_code_attribute(class, &main_method)
            .ok_or_else(|| {
                JvmError::RuntimeError(RuntimeError::UnsupportedOperation(
                    "Code attribute not found".to_string(),
                ))
            })?
            .clone();

        let max_stack = utils::read_u16(&code_attr.info, 0) as usize;
        let max_locals = utils::read_u16(&code_attr.info, 2) as usize;
        let code_length = utils::read_u32(&code_attr.info, 4) as usize;

        let mut frame = StackFrame::new(max_locals, max_stack, "main".to_string());
        let code = code_attr.info[8..8 + code_length].to_vec();
        let class_clone = class.clone();

        while frame.pc < code.len() {
            let opcode = code[frame.pc];
            frame.pc += 1;
            self.debugger.log_instruction(&frame, &class_clone, opcode);

            if !DISPATCH_TABLE[opcode as usize](self, &class_clone, &code, &mut frame, opcode)? {
                break;
            }
        }

        Ok(())
    }

    /// Find the Code attribute in a method
    pub(crate) fn find_code_attribute<'a>(
        &self,
        class: &ClassFile,
        method: &'a MethodInfo,
    ) -> Option<&'a AttributeInfo> {
        method
            .attributes
            .iter()
            .find(|attr| {
                attr.info.len() >= 8
                    && class.get_string(attr.attribute_name_index).as_deref() == Some("Code")
            })
            .or_else(|| method.attributes.iter().find(|attr| attr.info.len() >= 8))
    }

    /// Set array element value
    pub fn array_set_value(&mut self, array_ref: u32, index: i32, value: Value) -> Result<(), RuntimeError> {
        self.memory.heap.array_set(array_ref, index as usize, value)?;
        Ok(())
    }

    /// Set integer array element value (convenience method)
    pub fn array_set_int(&mut self, array_ref: u32, index: i32, value: i32) -> Result<(), RuntimeError> {
        self.memory.heap.array_set(array_ref, index as usize, Value::Int(value))?;
        Ok(())
    }

    /// Get array element value
    pub fn array_get_value(&mut self, array_ref: u32, index: i32) -> Result<Value, RuntimeError> {
        Ok(self.memory.heap.array_get(array_ref, index as usize)?)
    }

    /// Get integer array element value (convenience method)
    pub fn array_get_int(&mut self, array_ref: u32, index: i32) -> Result<i32, RuntimeError> {
        match self.memory.heap.array_get(array_ref, index as usize)? {
            Value::Int(val) => Ok(val),
            _ => Err(RuntimeError::InvalidTypeConversion("array element".to_string(), "int".to_string())),
        }
    }

    /// Get array length
    pub fn array_get_length(&mut self, array_ref: u32) -> Result<i32, RuntimeError> {
        Ok(self.memory.heap.array_length(array_ref)? as i32)
    }

    /// Create a new object array
    pub fn new_array_object(&mut self, component_type: &str, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::ReferenceArray(component_type.to_string(), vec![0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new boolean array
    pub fn new_array_bool(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::BooleanArray(vec![false; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new byte array
    pub fn new_array_byte(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::ByteArray(vec![0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new char array
    pub fn new_array_char(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::CharArray(vec![0u16; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new short array
    pub fn new_array_short(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::ShortArray(vec![0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new long array
    pub fn new_array_long(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::LongArray(vec![0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new float array
    pub fn new_array_float(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::FloatArray(vec![0.0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new double array
    pub fn new_array_double(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::DoubleArray(vec![0.0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }

    /// Create a new integer array (convenience method)
    pub fn new_array_int(&mut self, length: i32) -> Result<u32, RuntimeError> {
        if length < 0 {
            return Err(RuntimeError::NegativeArraySizeException(length));
        }
        let arr = crate::memory::HeapArray::IntArray(vec![0; length as usize]);
        Ok(self.memory.heap.allocate_array(arr))
    }
}

impl Default for Interpreter {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod test_invokedynamic;
#[cfg(test)]
mod tests;