//! Rich PDF object handle with manipulation, serialization, and factory methods
//!
//! Inspired by qpdf's QPDFObjectHandle, this module provides a comprehensive
//! wrapper around PDF objects with serialization, type checking, and creation APIs.

use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
use std::io::Write;

use super::error::{QpdfError, Result};
use crate::pdf::object::{Array, Dict, Name, ObjRef, Object, PdfString};

const NAME_SPECIAL: &[u8] = b"#/()<>[]{}%";
const MAX_PARSE_DEPTH: usize = 128;

fn encode_name(s: &str) -> String {
    let mut out = String::with_capacity(s.len() * 3);
    for b in s.bytes() {
        if b < 0x21 || b > 0x7E || NAME_SPECIAL.contains(&b) {
            out.push_str(&format!("#{:02X}", b));
        } else {
            out.push(b as char);
        }
    }
    out
}

fn needs_hex_string(bytes: &[u8]) -> bool {
    bytes
        .iter()
        .any(|&b| b < 32 || b > 126 || b == b'(' || b == b')' || b == b'\\')
}

fn escape_literal_string(s: &[u8]) -> String {
    let mut out = String::with_capacity(s.len() * 2);
    for &b in s {
        match b {
            b'\\' => out.push_str("\\\\"),
            b'(' => out.push_str("\\("),
            b')' => out.push_str("\\)"),
            b'\n' => out.push_str("\\n"),
            b'\r' => out.push_str("\\r"),
            b'\t' => out.push_str("\\t"),
            b'\x08' => out.push_str("\\b"),
            b'\x0c' => out.push_str("\\f"),
            _ if b >= 32 && b <= 126 => out.push(b as char),
            _ => out.push_str(&format!("\\{:03o}", b)),
        }
    }
    out
}

fn format_real(v: f64, precision: Option<u32>) -> String {
    if v.fract() == 0.0 && v.abs() < 1e15 {
        format!("{}", v as i64)
    } else {
        let prec = precision.unwrap_or(6) as usize;
        let s = format!("{:.1$}", v, prec);
        s.trim_end_matches('0').trim_end_matches('.').to_string()
    }
}

// ============================================================================
// Rectangle
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rectangle {
    pub llx: f64,
    pub lly: f64,
    pub urx: f64,
    pub ury: f64,
}

impl Rectangle {
    /// Create a rectangle from a 4-element array of numbers
    pub fn from_array(arr: &[Object]) -> Option<Self> {
        if arr.len() != 4 {
            return None;
        }
        let llx = arr.get(0)?.as_real()?;
        let lly = arr.get(1)?.as_real()?;
        let urx = arr.get(2)?.as_real()?;
        let ury = arr.get(3)?.as_real()?;
        Some(Self { llx, lly, urx, ury })
    }

    /// Convert to a 4-element PDF array
    pub fn to_array(&self) -> Array {
        vec![
            Object::Real(self.llx),
            Object::Real(self.lly),
            Object::Real(self.urx),
            Object::Real(self.ury),
        ]
    }

    pub fn width(&self) -> f64 {
        self.urx - self.llx
    }

    pub fn height(&self) -> f64 {
        self.ury - self.lly
    }

    pub fn is_valid(&self) -> bool {
        self.urx >= self.llx && self.ury >= self.lly
    }

    /// Create a rectangle from a PDF Object (must be an Array of 4 numbers)
    pub fn from_object(obj: &Object) -> Option<Self> {
        obj.as_array().and_then(|a| Self::from_array(a))
    }

    /// Convert to a PDF Array Object
    pub fn to_object(&self) -> Object {
        Object::Array(self.to_array())
    }
}

// ============================================================================
// PdfMatrix
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PdfMatrix {
    pub a: f64,
    pub b: f64,
    pub c: f64,
    pub d: f64,
    pub e: f64,
    pub f: f64,
}

impl PdfMatrix {
    pub fn identity() -> Self {
        Self {
            a: 1.0,
            b: 0.0,
            c: 0.0,
            d: 1.0,
            e: 0.0,
            f: 0.0,
        }
    }

    pub fn translate(tx: f64, ty: f64) -> Self {
        Self {
            a: 1.0,
            b: 0.0,
            c: 0.0,
            d: 1.0,
            e: tx,
            f: ty,
        }
    }

    pub fn scale(sx: f64, sy: f64) -> Self {
        Self {
            a: sx,
            b: 0.0,
            c: 0.0,
            d: sy,
            e: 0.0,
            f: 0.0,
        }
    }

    pub fn rotate(angle_rad: f64) -> Self {
        let c = angle_rad.cos();
        let s = angle_rad.sin();
        Self {
            a: c,
            b: s,
            c: -s,
            d: c,
            e: 0.0,
            f: 0.0,
        }
    }

    pub fn multiply(&self, other: &Self) -> Self {
        Self {
            a: self.a * other.a + self.b * other.c,
            b: self.a * other.b + self.b * other.d,
            c: self.c * other.a + self.d * other.c,
            d: self.c * other.b + self.d * other.d,
            e: self.e * other.a + self.f * other.c + other.e,
            f: self.e * other.b + self.f * other.d + other.f,
        }
    }

    pub fn transform_point(&self, x: f64, y: f64) -> (f64, f64) {
        (
            self.a * x + self.c * y + self.e,
            self.b * x + self.d * y + self.f,
        )
    }

    pub fn transform_rect(&self, r: &Rectangle) -> Rectangle {
        let (llx, lly) = self.transform_point(r.llx, r.lly);
        let (urx, ury) = self.transform_point(r.urx, r.ury);
        Rectangle {
            llx: llx.min(urx),
            lly: lly.min(ury),
            urx: llx.max(urx),
            ury: lly.max(ury),
        }
    }

    pub fn invert(&self) -> Result<Self> {
        let det = self.a * self.d - self.b * self.c;
        if det.abs() < 1e-15 {
            return Err(QpdfError::InvalidOperation(
                "Matrix is singular and cannot be inverted".to_string(),
            ));
        }
        let inv_det = 1.0 / det;
        Ok(Self {
            a: self.d * inv_det,
            b: -self.b * inv_det,
            c: -self.c * inv_det,
            d: self.a * inv_det,
            e: (self.c * self.f - self.d * self.e) * inv_det,
            f: (self.b * self.e - self.a * self.f) * inv_det,
        })
    }

    /// Create from a 6-element array of numbers
    pub fn from_array(arr: &[Object]) -> Option<Self> {
        if arr.len() != 6 {
            return None;
        }
        Some(Self {
            a: arr.get(0)?.as_real()?,
            b: arr.get(1)?.as_real()?,
            c: arr.get(2)?.as_real()?,
            d: arr.get(3)?.as_real()?,
            e: arr.get(4)?.as_real()?,
            f: arr.get(5)?.as_real()?,
        })
    }

    pub fn to_array(&self) -> Array {
        vec![
            Object::Real(self.a),
            Object::Real(self.b),
            Object::Real(self.c),
            Object::Real(self.d),
            Object::Real(self.e),
            Object::Real(self.f),
        ]
    }
}

// ============================================================================
// ObjectHandle
// ============================================================================

pub struct ObjectHandle {
    object: Object,
    obj_gen: Option<ObjRef>,
    description: Option<String>,
    real_precision: Option<u32>,
}

impl ObjectHandle {
    pub fn new_null() -> Self {
        Self {
            object: Object::Null,
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_bool(b: bool) -> Self {
        Self {
            object: Object::Bool(b),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_integer(v: i64) -> Self {
        Self {
            object: Object::Int(v),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_real(v: f64) -> Self {
        Self {
            object: Object::Real(v),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_real_with_precision(v: f64, precision: u32) -> Self {
        Self {
            object: Object::Real(v),
            obj_gen: None,
            description: None,
            real_precision: Some(precision),
        }
    }

    pub fn new_name(s: &str) -> Self {
        Self {
            object: Object::Name(Name::new(s)),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_string(s: &[u8]) -> Self {
        Self {
            object: Object::String(PdfString::new(s.to_vec())),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_unicode_string(utf8: &str) -> Self {
        let mut bytes = vec![0xFE, 0xFF];
        for c in utf8.chars() {
            let cp = c as u32;
            bytes.push((cp >> 8) as u8);
            bytes.push((cp & 0xFF) as u8);
        }
        Self::new_string(&bytes)
    }

    pub fn new_operator(s: &str) -> Self {
        Self::new_name(s)
    }

    pub fn new_inline_image(data: &[u8]) -> Self {
        let mut dict = Dict::new();
        dict.insert(Name::new("Length"), Object::Int(data.len() as i64));
        Self {
            object: Object::Stream {
                dict,
                data: data.to_vec(),
            },
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_array() -> Self {
        Self {
            object: Object::Array(Vec::new()),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_array_from(items: Vec<ObjectHandle>) -> Self {
        Self {
            object: Object::Array(items.into_iter().map(|h| h.into_object()).collect()),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_array_from_rect(r: Rectangle) -> Self {
        Self {
            object: Object::Array(r.to_array()),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_array_from_matrix(m: PdfMatrix) -> Self {
        Self {
            object: Object::Array(m.to_array()),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_dictionary() -> Self {
        Self {
            object: Object::Dict(HashMap::new()),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_dictionary_from(map: HashMap<String, ObjectHandle>) -> Self {
        let dict: Dict = map
            .into_iter()
            .map(|(k, v)| (Name::new(&k), v.into_object()))
            .collect();
        Self {
            object: Object::Dict(dict),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_stream(dict: Dict, data: Vec<u8>) -> Self {
        Self {
            object: Object::Stream { dict, data },
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn new_stream_with_data(data: Vec<u8>) -> Self {
        let mut dict = Dict::new();
        dict.insert(Name::new("Length"), Object::Int(data.len() as i64));
        Self::new_stream(dict, data)
    }

    pub fn new_ref(num: i32, generation: i32) -> Self {
        Self {
            object: Object::Ref(ObjRef::new(num, generation)),
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn parse(input: &str) -> Result<Self> {
        parse_object(input.as_bytes())
    }

    pub fn from_object(object: Object) -> Self {
        Self {
            object,
            obj_gen: None,
            description: None,
            real_precision: None,
        }
    }

    pub fn with_obj_gen(mut self, obj_gen: ObjRef) -> Self {
        self.obj_gen = Some(obj_gen);
        self
    }

    pub fn object(&self) -> &Object {
        &self.object
    }

    pub fn into_object(self) -> Object {
        self.object
    }

    pub fn obj_gen(&self) -> Option<ObjRef> {
        self.obj_gen
    }

    pub fn type_name(&self) -> &'static str {
        match &self.object {
            Object::Null => "null",
            Object::Bool(_) => "boolean",
            Object::Int(_) => "integer",
            Object::Real(_) => "real",
            Object::String(_) => "string",
            Object::Name(_) => "name",
            Object::Array(_) => "array",
            Object::Dict(_) => "dictionary",
            Object::Stream { .. } => "stream",
            Object::Ref(_) => "reference",
        }
    }

    pub fn is_null(&self) -> bool {
        matches!(self.object, Object::Null)
    }

    pub fn is_bool(&self) -> bool {
        matches!(self.object, Object::Bool(_))
    }

    pub fn is_integer(&self) -> bool {
        matches!(self.object, Object::Int(_))
    }

    pub fn is_real(&self) -> bool {
        matches!(self.object, Object::Real(_))
    }

    pub fn is_number(&self) -> bool {
        matches!(self.object, Object::Int(_) | Object::Real(_))
    }

    pub fn is_name(&self) -> bool {
        matches!(self.object, Object::Name(_))
    }

    pub fn is_string(&self) -> bool {
        matches!(self.object, Object::String(_))
    }

    pub fn is_array(&self) -> bool {
        matches!(self.object, Object::Array(_))
    }

    pub fn is_dictionary(&self) -> bool {
        matches!(self.object, Object::Dict(_))
    }

    pub fn is_stream(&self) -> bool {
        matches!(self.object, Object::Stream { .. })
    }

    pub fn is_indirect(&self) -> bool {
        matches!(self.object, Object::Ref(_))
    }

    pub fn is_scalar(&self) -> bool {
        matches!(
            self.object,
            Object::Null
                | Object::Bool(_)
                | Object::Int(_)
                | Object::Real(_)
                | Object::String(_)
                | Object::Name(_)
        )
    }

    pub fn is_name_and_equals(&self, name: &str) -> bool {
        self.object
            .as_name()
            .map(|n| n.as_str() == name)
            .unwrap_or(false)
    }

    pub fn is_dictionary_of_type(&self, type_name: &str, subtype: Option<&str>) -> bool {
        let d = match self.object.as_dict() {
            Some(d) => d,
            _ => return false,
        };
        let t = d.get(&Name::new("Type")).and_then(|o| o.as_name());
        if t.map(|n| n.as_str()) != Some(type_name) {
            return false;
        }
        if let Some(sub) = subtype {
            let s = d.get(&Name::new("Subtype")).and_then(|o| o.as_name());
            if s.map(|n| n.as_str()) != Some(sub) {
                return false;
            }
        }
        true
    }

    pub fn is_stream_of_type(&self, type_name: &str, subtype: Option<&str>) -> bool {
        match &self.object {
            Object::Stream { dict, .. } => {
                let t = dict.get(&Name::new("Type")).and_then(|o| o.as_name());
                if t.map(|n| n.as_str()) != Some(type_name) {
                    return false;
                }
                if let Some(sub) = subtype {
                    let s = dict.get(&Name::new("Subtype")).and_then(|o| o.as_name());
                    if s.map(|n| n.as_str()) != Some(sub) {
                        return false;
                    }
                }
                true
            }
            _ => false,
        }
    }

    pub fn is_rectangle(&self) -> bool {
        Rectangle::from_array(self.object.as_array().map(|a| a.as_slice()).unwrap_or(&[])).is_some()
    }

    pub fn is_matrix(&self) -> bool {
        PdfMatrix::from_array(self.object.as_array().map(|a| a.as_slice()).unwrap_or(&[])).is_some()
    }

    pub fn is_page_object(&self) -> bool {
        self.is_dictionary_of_type("Page", None)
    }

    pub fn is_form_xobject(&self) -> bool {
        self.is_stream_of_type("XObject", Some("Form"))
    }

    pub fn is_image(&self) -> bool {
        self.is_stream_of_type("XObject", Some("Image"))
    }

    pub fn get_bool_value(&self) -> Result<bool> {
        self.object.as_bool().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected boolean, got {}", self.type_name()))
        })
    }

    pub fn get_int_value(&self) -> Result<i64> {
        self.object.as_int().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected integer, got {}", self.type_name()))
        })
    }

    pub fn get_uint_value(&self) -> Result<u64> {
        let i = self.get_int_value()?;
        if i >= 0 {
            Ok(i as u64)
        } else {
            Err(QpdfError::InvalidOperation(format!(
                "expected non-negative integer, got {}",
                i
            )))
        }
    }

    pub fn get_real_value(&self) -> Result<f64> {
        self.object.as_real().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected real, got {}", self.type_name()))
        })
    }

    pub fn get_numeric_value(&self) -> Result<f64> {
        self.object.as_real().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected number, got {}", self.type_name()))
        })
    }

    pub fn get_name(&self) -> Result<String> {
        self.object
            .as_name()
            .map(|n| n.as_str().to_string())
            .ok_or_else(|| {
                QpdfError::InvalidOperation(format!("expected name, got {}", self.type_name()))
            })
    }

    pub fn get_string_value(&self) -> Result<Vec<u8>> {
        self.object
            .as_string()
            .map(|s| s.as_bytes().to_vec())
            .ok_or_else(|| {
                QpdfError::InvalidOperation(format!("expected string, got {}", self.type_name()))
            })
    }

    pub fn get_utf8_value(&self) -> Result<String> {
        let bytes = self.get_string_value()?;
        if bytes.len() >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF {
            let u16s: Vec<u16> = bytes[2..]
                .chunks_exact(2)
                .map(|c| u16::from_be_bytes([c[0], c[1]]))
                .collect();
            String::from_utf16(&u16s).map_err(|e| QpdfError::Parse(e.to_string()))
        } else {
            String::from_utf8(bytes).map_err(|e| QpdfError::Parse(e.to_string()))
        }
    }

    pub fn array_len(&self) -> Result<usize> {
        self.object.as_array().map(|a| a.len()).ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", self.type_name()))
        })
    }

    pub fn array_item(&self, index: usize) -> Result<ObjectHandle> {
        let arr = self.object.as_array().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", self.type_name()))
        })?;
        arr.get(index)
            .cloned()
            .map(ObjectHandle::from_object)
            .ok_or_else(|| QpdfError::NotFound(format!("array index {} out of bounds", index)))
    }

    pub fn array_as_vector(&self) -> Result<Vec<ObjectHandle>> {
        let arr = self.object.as_array().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", self.type_name()))
        })?;
        Ok(arr.iter().cloned().map(ObjectHandle::from_object).collect())
    }

    pub fn array_as_rectangle(&self) -> Result<Rectangle> {
        let arr = self.object.as_array().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", self.type_name()))
        })?;
        Rectangle::from_array(arr).ok_or_else(|| {
            QpdfError::InvalidOperation("array is not a valid 4-element rectangle".to_string())
        })
    }

    pub fn array_as_matrix(&self) -> Result<PdfMatrix> {
        let arr = self.object.as_array().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", self.type_name()))
        })?;
        PdfMatrix::from_array(arr).ok_or_else(|| {
            QpdfError::InvalidOperation("array is not a valid 6-element matrix".to_string())
        })
    }

    pub fn set_array_item(&mut self, index: usize, value: ObjectHandle) -> Result<()> {
        let type_name = self.type_name().to_string();
        let arr = self.object.as_array_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", type_name))
        })?;
        if index >= arr.len() {
            return Err(QpdfError::InvalidOperation(format!(
                "array index {} out of bounds (len {})",
                index,
                arr.len()
            )));
        }
        arr[index] = value.into_object();
        Ok(())
    }

    pub fn append_item(&mut self, value: ObjectHandle) -> Result<()> {
        let type_name = self.type_name().to_string();
        let arr = self.object.as_array_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", type_name))
        })?;
        arr.push(value.into_object());
        Ok(())
    }

    pub fn insert_item(&mut self, index: usize, value: ObjectHandle) -> Result<()> {
        let type_name = self.type_name().to_string();
        let arr = self.object.as_array_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", type_name))
        })?;
        if index > arr.len() {
            return Err(QpdfError::InvalidOperation(format!(
                "array index {} out of bounds (len {})",
                index,
                arr.len()
            )));
        }
        arr.insert(index, value.into_object());
        Ok(())
    }

    pub fn erase_item(&mut self, index: usize) -> Result<ObjectHandle> {
        let type_name = self.type_name().to_string();
        let arr = self.object.as_array_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", type_name))
        })?;
        if index >= arr.len() {
            return Err(QpdfError::InvalidOperation(format!(
                "array index {} out of bounds (len {})",
                index,
                arr.len()
            )));
        }
        Ok(ObjectHandle::from_object(arr.remove(index)))
    }

    pub fn set_array_from_vector(&mut self, items: Vec<ObjectHandle>) -> Result<()> {
        let type_name = self.type_name().to_string();
        let arr = self.object.as_array_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected array, got {}", type_name))
        })?;
        *arr = items.into_iter().map(|h| h.into_object()).collect();
        Ok(())
    }

    pub fn has_key(&self, key: &str) -> bool {
        self.object
            .as_dict()
            .map(|d| d.contains_key(&Name::new(key)))
            .unwrap_or(false)
    }

    pub fn get_key(&self, key: &str) -> Result<ObjectHandle> {
        self.object
            .as_dict()
            .and_then(|d| d.get(&Name::new(key)).cloned())
            .map(ObjectHandle::from_object)
            .ok_or_else(|| QpdfError::NotFound(format!("key '{}' not found", key)))
    }

    pub fn get_key_or_null(&self, key: &str) -> ObjectHandle {
        self.object
            .as_dict()
            .and_then(|d| d.get(&Name::new(key)).cloned())
            .map(ObjectHandle::from_object)
            .unwrap_or_else(ObjectHandle::new_null)
    }

    pub fn get_keys(&self) -> Vec<String> {
        self.object
            .as_dict()
            .map(|d| d.keys().map(|k| k.as_str().to_string()).collect())
            .unwrap_or_default()
    }

    pub fn get_dict_as_map(&self) -> Result<HashMap<String, ObjectHandle>> {
        let d = self.object.as_dict().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected dictionary, got {}", self.type_name()))
        })?;
        Ok(d.iter()
            .map(|(k, v)| (k.as_str().to_string(), ObjectHandle::from_object(v.clone())))
            .collect())
    }

    pub fn replace_key(&mut self, key: &str, value: ObjectHandle) -> Result<()> {
        let type_name = self.type_name().to_string();
        let d = self.object.as_dict_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected dictionary, got {}", type_name))
        })?;
        d.insert(Name::new(key), value.into_object());
        Ok(())
    }

    pub fn remove_key(&mut self, key: &str) -> Result<()> {
        let type_name = self.type_name().to_string();
        let d = self.object.as_dict_mut().ok_or_else(|| {
            QpdfError::InvalidOperation(format!("expected dictionary, got {}", type_name))
        })?;
        d.remove(&Name::new(key));
        Ok(())
    }

    pub fn replace_key_and_get_old(
        &mut self,
        key: &str,
        value: ObjectHandle,
    ) -> Option<ObjectHandle> {
        self.object
            .as_dict_mut()
            .and_then(|d| d.insert(Name::new(key), value.into_object()))
            .map(ObjectHandle::from_object)
    }

    pub fn get_stream_dict(&self) -> Result<ObjectHandle> {
        match &self.object {
            Object::Stream { dict, .. } => {
                Ok(ObjectHandle::from_object(Object::Dict(dict.clone())))
            }
            _ => Err(QpdfError::InvalidOperation(format!(
                "expected stream, got {}",
                self.type_name()
            ))),
        }
    }

    pub fn get_stream_data(&self) -> Result<Vec<u8>> {
        match &self.object {
            Object::Stream { data, .. } => Ok(data.clone()),
            _ => Err(QpdfError::InvalidOperation(format!(
                "expected stream, got {}",
                self.type_name()
            ))),
        }
    }

    pub fn replace_stream_dict(&mut self, dict_handle: ObjectHandle) -> Result<()> {
        let dict = dict_handle
            .into_object()
            .as_dict()
            .cloned()
            .ok_or_else(|| {
                QpdfError::InvalidOperation("expected dictionary for stream dict".to_string())
            })?;
        match &mut self.object {
            Object::Stream { dict: d, .. } => {
                *d = dict;
                Ok(())
            }
            _ => Err(QpdfError::InvalidOperation(format!(
                "expected stream, got {}",
                self.type_name()
            ))),
        }
    }

    pub fn replace_stream_data(
        &mut self,
        data: Vec<u8>,
        _filter: Option<ObjectHandle>,
        _decode_parms: Option<ObjectHandle>,
    ) -> Result<()> {
        match &mut self.object {
            Object::Stream { dict, data: d } => {
                dict.insert(Name::new("Length"), Object::Int(data.len() as i64));
                *d = data;
                Ok(())
            }
            _ => Err(QpdfError::InvalidOperation(format!(
                "expected stream, got {}",
                self.type_name()
            ))),
        }
    }

    pub fn merge_resources(
        &mut self,
        other: &ObjectHandle,
    ) -> Result<HashMap<String, HashMap<String, String>>> {
        let mut conflicts: HashMap<String, HashMap<String, String>> = HashMap::new();
        let (my_dict, other_dict) = match (self.object.as_dict_mut(), other.object.as_dict()) {
            (Some(m), Some(o)) => (m, o),
            _ => return Ok(conflicts),
        };
        let resources = my_dict
            .entry(Name::new("Resources"))
            .or_insert_with(|| Object::Dict(Dict::new()));
        let my_res = match resources {
            Object::Dict(d) => d,
            _ => return Ok(conflicts),
        };
        let other_res = match other_dict.get(&Name::new("Resources")) {
            Some(Object::Dict(d)) => d,
            _ => return Ok(conflicts),
        };
        for (cat_name, cat) in other_res {
            let cat_str = cat_name.as_str().to_string();
            let my_cat = my_res
                .entry(cat_name.clone())
                .or_insert_with(|| Object::Dict(Dict::new()));
            let my_cat_dict = match my_cat {
                Object::Dict(d) => d,
                _ => continue,
            };
            let other_cat_dict = match cat {
                Object::Dict(d) => d,
                _ => continue,
            };
            for (res_name, res_obj) in other_cat_dict {
                let name_str = res_name.as_str().to_string();
                if let Some(existing) = my_cat_dict.get(res_name) {
                    if existing != res_obj {
                        conflicts
                            .entry(cat_str.clone())
                            .or_default()
                            .insert(name_str, format!("{:?}", existing));
                    }
                } else {
                    my_cat_dict.insert(res_name.clone(), res_obj.clone());
                }
            }
        }
        Ok(conflicts)
    }

    pub fn get_resource_names(&self) -> Result<HashSet<String>> {
        let mut names = HashSet::new();
        let d = match self.object.as_dict() {
            Some(d) => d,
            _ => return Ok(names),
        };
        let res = match d.get(&Name::new("Resources")) {
            Some(Object::Dict(r)) => r,
            _ => return Ok(names),
        };
        for cat in res.values() {
            if let Object::Dict(cat_dict) = cat {
                for k in cat_dict.keys() {
                    names.insert(k.as_str().to_string());
                }
            }
        }
        if let Object::Stream { dict, .. } = &self.object {
            if let Some(Object::Dict(r)) = dict.get(&Name::new("Resources")) {
                for cat in r.values() {
                    if let Object::Dict(cat_dict) = cat {
                        for k in cat_dict.keys() {
                            names.insert(k.as_str().to_string());
                        }
                    }
                }
            }
        }
        Ok(names)
    }

    pub fn get_unique_resource_name(&self, prefix: &str, min_suffix: &mut i32) -> String {
        let existing = self.get_resource_names().unwrap_or_default();
        loop {
            let name = format!("{}{}", prefix, min_suffix);
            if !existing.contains(&name) {
                *min_suffix += 1;
                return name;
            }
            *min_suffix += 1;
        }
    }

    fn unparse_object(obj: &Object, real_precision: Option<u32>) -> String {
        match obj {
            Object::Null => "null".to_string(),
            Object::Bool(true) => "true".to_string(),
            Object::Bool(false) => "false".to_string(),
            Object::Int(i) => format!("{}", i),
            Object::Real(r) => format_real(*r, real_precision),
            Object::Name(n) => format!("/{}", encode_name(n.as_str())),
            Object::String(s) => {
                let bytes = s.as_bytes();
                if needs_hex_string(bytes) {
                    let hex: String = bytes.iter().map(|b| format!("{:02X}", b)).collect();
                    format!("<{}>", hex)
                } else {
                    format!("({})", escape_literal_string(bytes))
                }
            }
            Object::Array(arr) => {
                let parts: Vec<String> = arr
                    .iter()
                    .map(|o| Self::unparse_object(o, real_precision))
                    .collect();
                format!("[ {} ]", parts.join(" "))
            }
            Object::Dict(d) => {
                let parts: Vec<String> = d
                    .iter()
                    .map(|(k, v)| {
                        format!(
                            "/{} {}",
                            encode_name(k.as_str()),
                            Self::unparse_object(v, real_precision)
                        )
                    })
                    .collect();
                format!("<< {} >>", parts.join(" "))
            }
            Object::Stream { dict, data } => {
                let dict_str = {
                    let parts: Vec<String> = dict
                        .iter()
                        .map(|(k, v)| {
                            format!(
                                "/{} {}",
                                encode_name(k.as_str()),
                                Self::unparse_object(v, real_precision)
                            )
                        })
                        .collect();
                    format!("<< {} >>", parts.join(" "))
                };
                format!(
                    "{} stream\r\n{}\r\nendstream",
                    dict_str,
                    String::from_utf8_lossy(data)
                )
            }
            Object::Ref(r) => format!("{} {} R", r.num, r.generation),
        }
    }

    pub fn unparse(&self) -> String {
        Self::unparse_object(&self.object, self.real_precision)
    }

    pub fn unparse_binary(&self) -> Vec<u8> {
        self.unparse().into_bytes()
    }

    pub fn unparse_resolved(&self) -> String {
        self.unparse()
    }

    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
        w.write_all(self.unparse_binary().as_slice())?;
        Ok(())
    }

    pub fn shallow_copy(&self) -> Self {
        Self {
            object: self.object.clone(),
            obj_gen: self.obj_gen,
            description: self.description.clone(),
            real_precision: self.real_precision,
        }
    }

    pub fn make_direct(&self) -> Self {
        self.shallow_copy()
    }
}

impl fmt::Display for ObjectHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.unparse())
    }
}

impl From<Object> for ObjectHandle {
    fn from(obj: Object) -> Self {
        Self::from_object(obj)
    }
}

// ============================================================================
// Parse implementation
// ============================================================================

fn skip_whitespace(data: &[u8]) -> usize {
    let mut pos = 0;
    while pos < data.len() {
        let b = data[pos];
        if matches!(b, 0 | 9 | 10 | 12 | 13 | 32) {
            pos += 1;
        } else if b == b'%' {
            while pos < data.len() && data[pos] != b'\n' && data[pos] != b'\r' {
                pos += 1;
            }
        } else {
            break;
        }
    }
    pos
}

fn parse_object(data: &[u8]) -> Result<ObjectHandle> {
    let mut pos = skip_whitespace(data);
    if pos >= data.len() {
        return Err(QpdfError::Parse("Unexpected end of input".to_string()));
    }
    let (obj, len) = parse_value(&data[pos..])?;
    pos += len;
    skip_whitespace(&data[pos..]);
    Ok(ObjectHandle::from_object(obj))
}

fn parse_value(data: &[u8]) -> Result<(Object, usize)> {
    parse_value_inner(data, 0)
}

fn parse_value_inner(data: &[u8], depth: usize) -> Result<(Object, usize)> {
    if depth > MAX_PARSE_DEPTH {
        return Err(QpdfError::Parse(
            "Maximum nesting depth exceeded".to_string(),
        ));
    }
    let mut pos = skip_whitespace(data);
    if pos >= data.len() {
        return Err(QpdfError::Parse("Unexpected end of input".to_string()));
    }

    if data[pos..].starts_with(b"<<") {
        let (dict, len) = parse_dict(&data[pos..], depth)?;
        pos += len;
        let after = pos + skip_whitespace(&data[pos..]);
        if data
            .get(after..)
            .map(|s| s.starts_with(b"stream"))
            .unwrap_or(false)
        {
            let (stream_data, stream_len) = parse_stream(&data[after..])?;
            return Ok((
                Object::Stream {
                    dict,
                    data: stream_data,
                },
                after + stream_len,
            ));
        }
        return Ok((Object::Dict(dict), pos - skip_whitespace(data)));
    }

    if data[pos..].starts_with(b"[") {
        pos += 1;
        let mut arr = Array::new();
        loop {
            pos += skip_whitespace(&data[pos..]);
            if data.get(pos) == Some(&b']') {
                pos += 1;
                return Ok((Object::Array(arr), pos));
            }
            let (obj, len) = parse_value_inner(&data[pos..], depth + 1)?;
            pos += len;
            arr.push(obj);
        }
    }

    if data[pos].is_ascii_digit()
        || data.get(pos) == Some(&b'-')
        || data.get(pos) == Some(&b'+')
        || data.get(pos) == Some(&b'.')
    {
        let (num, l1) = parse_number(&data[pos..])?;
        let after_num = pos + l1;
        let ws = skip_whitespace(&data[after_num..]);
        let after_ws = after_num + ws;
        if after_ws < data.len() {
            let rest = &data[after_ws..];
            if rest.len() >= 2 {
                let (generation, l2) = parse_int(&rest)?;
                let after_gen = after_ws + l2;
                let ws2 = skip_whitespace(&data[after_gen..]);
                if data.get(after_gen + ws2) == Some(&b'R') {
                    return Ok((
                        Object::Ref(ObjRef::new(num as i32, generation as i32)),
                        after_gen + ws2 + 1 - pos,
                    ));
                }
            }
        }
        let obj = if num.fract() == 0.0 && num.abs() < 1e15 {
            Object::Int(num as i64)
        } else {
            Object::Real(num)
        };
        return Ok((obj, pos + l1 - pos));
    }

    if data[pos..].starts_with(b"null") {
        let end = pos + 4;
        if end <= data.len() && (end == data.len() || !data[end].is_ascii_alphanumeric()) {
            return Ok((Object::Null, 4));
        }
    }
    if data[pos..].starts_with(b"true") {
        let end = pos + 4;
        if end <= data.len() && (end == data.len() || !data[end].is_ascii_alphanumeric()) {
            return Ok((Object::Bool(true), 4));
        }
    }
    if data[pos..].starts_with(b"false") {
        let end = pos + 5;
        if end <= data.len() && (end == data.len() || !data[end].is_ascii_alphanumeric()) {
            return Ok((Object::Bool(false), 5));
        }
    }

    if data.get(pos) == Some(&b'/') {
        let (name, len) = parse_name(&data[pos..])?;
        return Ok((Object::Name(name), len));
    }

    if data.get(pos) == Some(&b'(') {
        let (s, len) = parse_literal_string(&data[pos..])?;
        return Ok((Object::String(PdfString::new(s)), len));
    }

    if data.get(pos) == Some(&b'<') && data.get(pos + 1) != Some(&b'<') {
        let (s, len) = parse_hex_string(&data[pos..])?;
        return Ok((Object::String(PdfString::new(s)), len));
    }

    Err(QpdfError::Parse(format!(
        "Unknown value type at byte {}",
        pos
    )))
}

fn parse_int(data: &[u8]) -> Result<(i64, usize)> {
    let mut pos = 0;
    let neg = data.get(pos) == Some(&b'-');
    if neg || data.get(pos) == Some(&b'+') {
        pos += 1;
    }
    let start = pos;
    while pos < data.len() && data[pos].is_ascii_digit() {
        pos += 1;
    }
    let s = std::str::from_utf8(&data[start..pos])
        .map_err(|_| QpdfError::Parse("Invalid int".to_string()))?;
    let n: i64 = s
        .parse()
        .map_err(|_| QpdfError::Parse("Invalid int".to_string()))?;
    Ok((if neg { -n } else { n }, pos))
}

fn parse_number(data: &[u8]) -> Result<(f64, usize)> {
    let mut pos = 0;
    let neg = data.get(pos) == Some(&b'-');
    if neg || data.get(pos) == Some(&b'+') {
        pos += 1;
    }
    let start = pos;
    while pos < data.len() && (data[pos].is_ascii_digit() || data[pos] == b'.') {
        pos += 1;
    }
    let s = std::str::from_utf8(&data[start..pos])
        .map_err(|_| QpdfError::Parse("Invalid number".to_string()))?;
    let n: f64 = s
        .parse()
        .map_err(|_| QpdfError::Parse("Invalid number".to_string()))?;
    Ok((if neg { -n } else { n }, pos))
}

fn parse_dict(data: &[u8], depth: usize) -> Result<(Dict, usize)> {
    let mut pos = skip_whitespace(data);
    if !data[pos..].starts_with(b"<<") {
        return Err(QpdfError::Parse("Expected <<".to_string()));
    }
    pos += 2;
    let mut dict = Dict::new();
    loop {
        pos += skip_whitespace(&data[pos..]);
        if data[pos..].starts_with(b">>") {
            pos += 2;
            return Ok((dict, pos));
        }
        let (name, l1) = parse_name(&data[pos..])?;
        pos += l1;
        pos += skip_whitespace(&data[pos..]);
        let (obj, l2) = parse_value_inner(&data[pos..], depth + 1)?;
        pos += l2;
        dict.insert(name, obj);
    }
}

fn parse_name(data: &[u8]) -> Result<(Name, usize)> {
    if data.get(0) != Some(&b'/') {
        return Err(QpdfError::Parse("Expected / for name".to_string()));
    }
    let mut pos = 1;
    let mut result = String::new();
    while pos < data.len() {
        let b = data[pos];
        if b.is_ascii_whitespace()
            || b == b'/'
            || b == b'['
            || b == b']'
            || b == b'<'
            || b == b'>'
            || b == b'('
            || b == b')'
            || b == b'{'
            || b == b'}'
            || b == b'%'
        {
            break;
        }
        if b == b'#' && pos + 2 < data.len() {
            let hex = std::str::from_utf8(&data[pos + 1..pos + 3])
                .map_err(|_| QpdfError::Parse("Invalid hex in name".to_string()))?;
            if let Ok(byte) = u8::from_str_radix(hex, 16) {
                result.push(byte as char);
                pos += 3;
                continue;
            }
        }
        result.push(b as char);
        pos += 1;
    }
    Ok((Name::new(&result), pos))
}

fn parse_literal_string(data: &[u8]) -> Result<(Vec<u8>, usize)> {
    if data.get(0) != Some(&b'(') {
        return Err(QpdfError::Parse(
            "Expected ( for literal string".to_string(),
        ));
    }
    let mut pos = 1;
    let mut result = Vec::new();
    let mut depth = 1;
    while pos < data.len() {
        let b = data[pos];
        match b {
            b'(' => {
                depth += 1;
                result.push(b);
            }
            b')' => {
                depth -= 1;
                if depth == 0 {
                    pos += 1;
                    return Ok((result, pos));
                }
                result.push(b);
            }
            b'\\' => {
                pos += 1;
                if let Some(escaped) = data.get(pos) {
                    pos += 1;
                    match escaped {
                        b'n' => result.push(b'\n'),
                        b'r' => result.push(b'\r'),
                        b't' => result.push(b'\t'),
                        b'b' => result.push(b'\x08'),
                        b'f' => result.push(b'\x0c'),
                        b'(' => result.push(b'('),
                        b')' => result.push(b')'),
                        b'\\' => result.push(b'\\'),
                        b'\n' | b'\r' => {}
                        b'0'..=b'7' => {
                            let mut octal = (escaped - b'0') as u32;
                            for _ in 0..2 {
                                if let Some(ob) = data.get(pos) {
                                    if (b'0'..=b'7').contains(ob) {
                                        pos += 1;
                                        octal = octal * 8 + (ob - b'0') as u32;
                                    } else {
                                        break;
                                    }
                                }
                            }
                            if octal <= 255 {
                                result.push(octal as u8);
                            }
                        }
                        _ => result.push(*escaped),
                    }
                }
                continue;
            }
            _ => result.push(b),
        }
        pos += 1;
    }
    Err(QpdfError::Parse("Unterminated literal string".to_string()))
}

fn parse_hex_string(data: &[u8]) -> Result<(Vec<u8>, usize)> {
    if data.get(0) != Some(&b'<') {
        return Err(QpdfError::Parse("Expected < for hex string".to_string()));
    }
    let mut pos = 1;
    let mut hex_chars = String::new();
    while pos < data.len() {
        let b = data[pos];
        if b == b'>' {
            pos += 1;
            break;
        }
        if !b.is_ascii_whitespace() {
            if b.is_ascii_hexdigit() {
                hex_chars.push(b as char);
            } else {
                return Err(QpdfError::Parse(
                    "Invalid character in hex string".to_string(),
                ));
            }
        }
        pos += 1;
    }
    if hex_chars.len() % 2 != 0 {
        hex_chars.push('0');
    }
    let mut result = Vec::new();
    let mut chars = hex_chars.chars();
    while let (Some(h1), Some(h2)) = (chars.next(), chars.next()) {
        let hex = format!("{}{}", h1, h2);
        if let Ok(byte) = u8::from_str_radix(&hex, 16) {
            result.push(byte);
        }
    }
    Ok((result, pos))
}

fn parse_stream(data: &[u8]) -> Result<(Vec<u8>, usize)> {
    if !data.starts_with(b"stream") {
        return Err(QpdfError::Parse("Expected stream".to_string()));
    }
    let mut pos = 6;
    if data.get(pos) == Some(&b'\r') {
        pos += 1;
    }
    if data.get(pos) == Some(&b'\n') {
        pos += 1;
    }
    let start = pos;
    while pos < data.len() {
        if pos + 9 <= data.len() && &data[pos..pos + 9] == b"endstream" {
            let data_len = pos - start;
            return Ok((data[start..pos].to_vec(), pos + 9));
        }
        pos += 1;
    }
    Err(QpdfError::Parse("Unterminated stream".to_string()))
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_rectangle_from_array_to_array() {
        let r = Rectangle {
            llx: 0.0,
            lly: 0.0,
            urx: 100.0,
            ury: 200.0,
        };
        let arr = r.to_array();
        let r2 = Rectangle::from_array(&arr).unwrap();
        assert_eq!(r, r2);
        assert_eq!(r.width(), 100.0);
        assert_eq!(r.height(), 200.0);
        assert!(r.is_valid());
    }

    #[test]
    fn test_rectangle_invalid() {
        let r = Rectangle {
            llx: 100.0,
            lly: 200.0,
            urx: 50.0,
            ury: 100.0,
        };
        assert!(!r.is_valid());
    }

    #[test]
    fn test_matrix_identity() {
        let m = PdfMatrix::identity();
        assert_eq!(m.a, 1.0);
        assert_eq!(m.d, 1.0);
        let (x, y) = m.transform_point(1.0, 0.0);
        assert_eq!((x, y), (1.0, 0.0));
    }

    #[test]
    fn test_matrix_translate_scale_rotate() {
        let t = PdfMatrix::translate(10.0, 20.0);
        let (x, y) = t.transform_point(0.0, 0.0);
        assert_eq!((x, y), (10.0, 20.0));

        let s = PdfMatrix::scale(2.0, 3.0);
        let (x, y) = s.transform_point(1.0, 1.0);
        assert_eq!((x, y), (2.0, 3.0));

        let r = PdfMatrix::rotate(std::f64::consts::PI / 2.0);
        let (x, y) = r.transform_point(1.0, 0.0);
        assert!((x - 0.0).abs() < 1e-10);
        assert!((y - 1.0).abs() < 1e-10);
    }

    #[test]
    fn test_matrix_multiply() {
        let m1 = PdfMatrix::scale(2.0, 2.0);
        let m2 = PdfMatrix::translate(1.0, 2.0);
        let m = m1.multiply(&m2);
        let (x, y) = m.transform_point(0.0, 0.0);
        assert_eq!((x, y), (1.0, 2.0));
    }

    #[test]
    fn test_matrix_invert() {
        let m = PdfMatrix::scale(2.0, 3.0);
        let inv = m.invert().unwrap();
        let composed = m.multiply(&inv);
        assert!((composed.a - 1.0).abs() < 1e-10);
        assert!((composed.d - 1.0).abs() < 1e-10);
    }

    #[test]
    fn test_matrix_from_array_to_array() {
        let m = PdfMatrix::identity();
        let arr = m.to_array();
        let m2 = PdfMatrix::from_array(&arr).unwrap();
        assert_eq!(m, m2);
    }

    #[test]
    fn test_factory_methods() {
        let _ = ObjectHandle::new_null();
        let _ = ObjectHandle::new_bool(true);
        let _ = ObjectHandle::new_integer(1);
        let _ = ObjectHandle::new_real(1.5);
        let _ = ObjectHandle::new_name("Test");
        let _ = ObjectHandle::new_string(b"x");
        let _ = ObjectHandle::new_unicode_string("Hello");
        let _ = ObjectHandle::new_operator("Tj");
        let _ = ObjectHandle::new_inline_image(b"data");
        let _ = ObjectHandle::new_array();
        let _ = ObjectHandle::new_array_from(vec![ObjectHandle::new_integer(1)]);
        let _ = ObjectHandle::new_array_from_rect(Rectangle {
            llx: 0.0,
            lly: 0.0,
            urx: 612.0,
            ury: 792.0,
        });
        let _ = ObjectHandle::new_array_from_matrix(PdfMatrix::identity());
        let _ = ObjectHandle::new_dictionary();
        let mut map = HashMap::new();
        map.insert("K".to_string(), ObjectHandle::new_integer(42));
        let _ = ObjectHandle::new_dictionary_from(map);
        let _ = ObjectHandle::new_stream(Dict::new(), vec![]);
        let _ = ObjectHandle::new_stream_with_data(vec![1, 2, 3]);
        let _ = ObjectHandle::new_ref(10, 0);
    }

    #[test]
    fn test_type_checks() {
        assert!(ObjectHandle::new_null().is_null());
        assert!(ObjectHandle::new_bool(true).is_bool());
        assert!(ObjectHandle::new_integer(1).is_integer());
        assert!(ObjectHandle::new_real(1.0).is_real());
        assert!(ObjectHandle::new_name("X").is_name());
        assert!(ObjectHandle::new_string(b"x").is_string());
        assert!(ObjectHandle::new_array().is_array());
        assert!(ObjectHandle::new_dictionary().is_dictionary());
        assert!(ObjectHandle::new_stream_with_data(vec![]).is_stream());
        assert!(ObjectHandle::new_ref(1, 0).is_indirect());
        assert!(ObjectHandle::new_integer(1).is_scalar());
    }

    #[test]
    fn test_type_name() {
        assert_eq!(ObjectHandle::new_null().type_name(), "null");
        assert_eq!(ObjectHandle::new_bool(true).type_name(), "boolean");
        assert_eq!(ObjectHandle::new_integer(1).type_name(), "integer");
        assert_eq!(ObjectHandle::new_name("X").type_name(), "name");
    }

    #[test]
    fn test_value_accessors() {
        let h = ObjectHandle::new_bool(true);
        assert_eq!(h.get_bool_value().unwrap(), true);

        let h = ObjectHandle::new_integer(42);
        assert_eq!(h.get_int_value().unwrap(), 42);
        assert_eq!(h.get_uint_value().unwrap(), 42);

        let h = ObjectHandle::new_real(3.14);
        assert_eq!(h.get_real_value().unwrap(), 3.14);
        assert_eq!(h.get_numeric_value().unwrap(), 3.14);

        let h = ObjectHandle::new_name("Type");
        assert_eq!(h.get_name().unwrap(), "Type");

        let h = ObjectHandle::new_string(b"Hello");
        assert_eq!(h.get_string_value().unwrap(), b"Hello");
        assert_eq!(h.get_utf8_value().unwrap(), "Hello");
    }

    #[test]
    fn test_value_accessors_type_mismatch() {
        let h = ObjectHandle::new_null();
        assert!(h.get_bool_value().is_err());
        assert!(h.get_int_value().is_err());
    }

    #[test]
    fn test_array_operations() {
        let mut h = ObjectHandle::new_array();
        h.append_item(ObjectHandle::new_integer(1)).unwrap();
        h.append_item(ObjectHandle::new_integer(2)).unwrap();
        h.append_item(ObjectHandle::new_integer(3)).unwrap();
        assert_eq!(h.array_len().unwrap(), 3);
        assert_eq!(h.array_item(1).unwrap().get_int_value().unwrap(), 2);
        let vec = h.array_as_vector().unwrap();
        assert_eq!(vec.len(), 3);
        h.set_array_item(1, ObjectHandle::new_integer(99)).unwrap();
        assert_eq!(h.array_item(1).unwrap().get_int_value().unwrap(), 99);
        h.insert_item(0, ObjectHandle::new_integer(0)).unwrap();
        assert_eq!(h.array_len().unwrap(), 4);
        let removed = h.erase_item(0).unwrap();
        assert_eq!(removed.get_int_value().unwrap(), 0);
    }

    #[test]
    fn test_array_as_rectangle_matrix() {
        let h = ObjectHandle::new_array_from_rect(Rectangle {
            llx: 0.0,
            lly: 0.0,
            urx: 612.0,
            ury: 792.0,
        });
        let r = h.array_as_rectangle().unwrap();
        assert_eq!(r.urx, 612.0);

        let h = ObjectHandle::new_array_from_matrix(PdfMatrix::identity());
        let m = h.array_as_matrix().unwrap();
        assert_eq!(m.a, 1.0);
    }

    #[test]
    fn test_dict_operations() {
        let mut h = ObjectHandle::new_dictionary();
        assert!(!h.has_key("K"));
        h.replace_key("K", ObjectHandle::new_integer(42)).unwrap();
        assert!(h.has_key("K"));
        assert_eq!(h.get_key("K").unwrap().get_int_value().unwrap(), 42);
        assert!(h.get_key_or_null("Missing").is_null());
        let old = h
            .replace_key_and_get_old("K", ObjectHandle::new_integer(99))
            .unwrap();
        assert_eq!(old.get_int_value().unwrap(), 42);
        h.remove_key("K").unwrap();
        assert!(!h.has_key("K"));
        let keys = h.get_keys();
        assert!(keys.is_empty());
    }

    #[test]
    fn test_get_dict_as_map() {
        let mut h = ObjectHandle::new_dictionary();
        h.replace_key("A", ObjectHandle::new_integer(1)).unwrap();
        h.replace_key("B", ObjectHandle::new_name("X")).unwrap();
        let map = h.get_dict_as_map().unwrap();
        assert_eq!(map.len(), 2);
        assert_eq!(map.get("A").unwrap().get_int_value().unwrap(), 1);
    }

    #[test]
    fn test_stream_operations() {
        let h = ObjectHandle::new_stream_with_data(vec![1, 2, 3, 4, 5]);
        let dict = h.get_stream_dict().unwrap();
        assert!(dict.has_key("Length"));
        let data = h.get_stream_data().unwrap();
        assert_eq!(data, vec![1, 2, 3, 4, 5]);

        let mut h = ObjectHandle::new_stream_with_data(vec![1, 2, 3]);
        h.replace_stream_data(vec![4, 5, 6, 7], None, None).unwrap();
        let data = h.get_stream_data().unwrap();
        assert_eq!(data, vec![4, 5, 6, 7]);
    }

    #[test]
    fn test_resource_operations() {
        let mut h = ObjectHandle::new_dictionary();
        let mut res = Dict::new();
        let mut font = Dict::new();
        font.insert(Name::new("F1"), Object::Ref(ObjRef::new(5, 0)));
        res.insert(Name::new("Font"), Object::Dict(font));
        h.replace_key("Resources", ObjectHandle::from_object(Object::Dict(res)))
            .unwrap();
        let names = h.get_resource_names().unwrap();
        assert!(names.contains("F1"));

        let mut suffix = 0i32;
        let name = h.get_unique_resource_name("F", &mut suffix);
        assert_eq!(name, "F0");
    }

    #[test]
    fn test_merge_resources() {
        let mut h1 = ObjectHandle::new_dictionary();
        let mut res1 = Dict::new();
        let mut font1 = Dict::new();
        font1.insert(Name::new("F1"), Object::Ref(ObjRef::new(1, 0)));
        res1.insert(Name::new("Font"), Object::Dict(font1));
        h1.replace_key("Resources", ObjectHandle::from_object(Object::Dict(res1)))
            .unwrap();

        let mut h2 = ObjectHandle::new_dictionary();
        let mut res2 = Dict::new();
        let mut font2 = Dict::new();
        font2.insert(Name::new("F2"), Object::Ref(ObjRef::new(2, 0)));
        res2.insert(Name::new("Font"), Object::Dict(font2));
        h2.replace_key("Resources", ObjectHandle::from_object(Object::Dict(res2)))
            .unwrap();

        let conflicts = h1.merge_resources(&h2).unwrap();
        let names = h1.get_resource_names().unwrap();
        assert!(names.contains("F1"));
        assert!(names.contains("F2"));
    }

    #[test]
    fn test_serialization_null() {
        assert_eq!(ObjectHandle::new_null().unparse(), "null");
    }

    #[test]
    fn test_serialization_bool() {
        assert_eq!(ObjectHandle::new_bool(true).unparse(), "true");
        assert_eq!(ObjectHandle::new_bool(false).unparse(), "false");
    }

    #[test]
    fn test_serialization_int() {
        assert_eq!(ObjectHandle::new_integer(42).unparse(), "42");
        assert_eq!(ObjectHandle::new_integer(-10).unparse(), "-10");
    }

    #[test]
    fn test_serialization_real() {
        assert_eq!(ObjectHandle::new_real(3.14).unparse(), "3.14");
        assert_eq!(ObjectHandle::new_real(1.0).unparse(), "1");
    }

    #[test]
    fn test_serialization_name() {
        assert_eq!(ObjectHandle::new_name("Type").unparse(), "/Type");
    }

    #[test]
    fn test_serialization_name_special_chars() {
        let h = ObjectHandle::new_name("Name#With#Hash");
        assert!(h.unparse().contains("#23"));
    }

    #[test]
    fn test_serialization_string_literal() {
        let h = ObjectHandle::new_string(b"Hello");
        assert_eq!(h.unparse(), "(Hello)");
    }

    #[test]
    fn test_serialization_string_hex() {
        let h = ObjectHandle::new_string(&[0x00, 0xFF]);
        assert!(h.unparse().starts_with('<'));
        assert!(h.unparse().ends_with('>'));
    }

    #[test]
    fn test_serialization_array() {
        let h = ObjectHandle::new_array_from(vec![
            ObjectHandle::new_integer(1),
            ObjectHandle::new_integer(2),
            ObjectHandle::new_integer(3),
        ]);
        assert_eq!(h.unparse(), "[ 1 2 3 ]");
    }

    #[test]
    fn test_serialization_dict() {
        let mut h = ObjectHandle::new_dictionary();
        h.replace_key("Type", ObjectHandle::new_name("Page"))
            .unwrap();
        h.replace_key(
            "MediaBox",
            ObjectHandle::new_array_from_rect(Rectangle {
                llx: 0.0,
                lly: 0.0,
                urx: 612.0,
                ury: 792.0,
            }),
        )
        .unwrap();
        let s = h.unparse();
        assert!(s.contains("/Type"));
        assert!(s.contains("/Page"));
        assert!(s.contains("/MediaBox"));
    }

    #[test]
    fn test_serialization_ref() {
        assert_eq!(ObjectHandle::new_ref(10, 0).unparse(), "10 0 R");
    }

    #[test]
    fn test_parse_null() {
        let h = ObjectHandle::parse("null").unwrap();
        assert!(h.is_null());
    }

    #[test]
    fn test_parse_bool() {
        let h = ObjectHandle::parse("true").unwrap();
        assert_eq!(h.get_bool_value().unwrap(), true);
        let h = ObjectHandle::parse("false").unwrap();
        assert_eq!(h.get_bool_value().unwrap(), false);
    }

    #[test]
    fn test_parse_int() {
        let h = ObjectHandle::parse("42").unwrap();
        assert_eq!(h.get_int_value().unwrap(), 42);
        let h = ObjectHandle::parse("-7").unwrap();
        assert_eq!(h.get_int_value().unwrap(), -7);
    }

    #[test]
    fn test_parse_real() {
        let h = ObjectHandle::parse("3.14").unwrap();
        assert!((h.get_real_value().unwrap() - 3.14).abs() < 1e-10);
    }

    #[test]
    fn test_parse_name() {
        let h = ObjectHandle::parse("/Type").unwrap();
        assert_eq!(h.get_name().unwrap(), "Type");
    }

    #[test]
    fn test_parse_string() {
        let h = ObjectHandle::parse("(Hello)").unwrap();
        assert_eq!(h.get_utf8_value().unwrap(), "Hello");
    }

    #[test]
    fn test_parse_array() {
        let h = ObjectHandle::parse("[ 1 2 3 ]").unwrap();
        assert_eq!(h.array_len().unwrap(), 3);
        assert_eq!(h.array_item(0).unwrap().get_int_value().unwrap(), 1);
    }

    #[test]
    fn test_parse_dict() {
        let h = ObjectHandle::parse("<< /Type /Page >>").unwrap();
        assert!(h.is_dictionary());
        assert_eq!(h.get_key("Type").unwrap().get_name().unwrap(), "Page");
    }

    #[test]
    fn test_parse_ref() {
        let h = ObjectHandle::parse("10 0 R").unwrap();
        assert!(h.is_indirect());
        if let Object::Ref(r) = h.object() {
            assert_eq!(r.num, 10);
            assert_eq!(r.generation, 0);
        } else {
            panic!("expected Ref");
        }
    }

    #[test]
    fn test_shallow_copy() {
        let h = ObjectHandle::new_integer(42);
        let c = h.shallow_copy();
        assert_eq!(c.get_int_value().unwrap(), 42);
    }

    #[test]
    fn test_make_direct() {
        let h = ObjectHandle::new_integer(42);
        let d = h.make_direct();
        assert_eq!(d.get_int_value().unwrap(), 42);
    }

    #[test]
    fn test_with_obj_gen() {
        let h = ObjectHandle::new_integer(42).with_obj_gen(ObjRef::new(5, 0));
        assert_eq!(h.obj_gen(), Some(ObjRef::new(5, 0)));
    }

    #[test]
    fn test_from_object() {
        let obj = Object::Int(99);
        let h = ObjectHandle::from_object(obj);
        assert_eq!(h.get_int_value().unwrap(), 99);
    }

    #[test]
    fn test_is_page_object() {
        let mut h = ObjectHandle::new_dictionary();
        h.replace_key("Type", ObjectHandle::new_name("Page"))
            .unwrap();
        assert!(h.is_page_object());
    }

    #[test]
    fn test_is_form_xobject() {
        let mut dict = Dict::new();
        dict.insert(Name::new("Type"), Object::Name(Name::new("XObject")));
        dict.insert(Name::new("Subtype"), Object::Name(Name::new("Form")));
        let h = ObjectHandle::new_stream(dict, vec![]);
        assert!(h.is_form_xobject());
    }

    #[test]
    fn test_is_image() {
        let mut dict = Dict::new();
        dict.insert(Name::new("Type"), Object::Name(Name::new("XObject")));
        dict.insert(Name::new("Subtype"), Object::Name(Name::new("Image")));
        let h = ObjectHandle::new_stream(dict, vec![]);
        assert!(h.is_image());
    }

    #[test]
    fn test_display() {
        let h = ObjectHandle::new_integer(99);
        assert_eq!(format!("{}", h), "99");
    }
}
