//! DXF section writers
//!
//! This module contains writers for each section of a DXF file:
//! HEADER, CLASSES, TABLES, BLOCKS, ENTITIES, and OBJECTS.

use crate::document::CadDocument;
use crate::entities::*;
use crate::error::Result;
use crate::objects::{
    Dictionary, DictionaryVariable, DictionaryWithDefault, Group, ImageDefinition,
    ImageDefinitionReactor, Layout, MLineStyle, Material, MultiLeaderStyle,
    ObjectType, PlotSettings, RasterVariables, Scale, SortEntitiesTable, SpatialFilter,
    TableStyle, VisualStyle, BookColor, WipeoutVariables, XRecord,
};
use crate::tables::*;
use crate::types::{Color, DxfVersion, Handle, Vector3};
use crate::xdata::{ExtendedData, XDataValue};

use std::collections::HashSet;
use super::stream_writer::{DxfStreamWriter, DxfStreamWriterExt};

/// Sanitize a symbol table record name: strip control characters and
/// characters forbidden by AutoCAD (`< > / \ " : ; ? * | , = \``).
fn sanitize_symbol_name(name: &str) -> String {
    name.chars()
        .filter(|c| !c.is_control() && !matches!(c, '<' | '>' | '/' | '\\' | '"' | ':' | ';' | '?' | '*' | '|' | ',' | '=' | '`'))
        .collect()
}

/// Writes all DXF sections
pub struct SectionWriter<'a, W: DxfStreamWriter> {
    writer: &'a mut W,
    next_handle: u64,
    handle_seed: u64,
    /// DXF version (determines SAT vs SAB format for ACIS data)
    dxf_version: DxfVersion,
    /// Collected SAB entries: (entity_handle, sab_binary_data) for ACDSDATA section
    sab_entries: Vec<(Handle, Vec<u8>)>,
    /// Whether currently writing paper space entities (for group code 67)
    writing_paper_space: bool,
    /// Set of all handles that will exist in the output DXF.
    /// Used to filter reactor/xdictionary references to non-existent objects.
    valid_handles: HashSet<Handle>,
    /// Handle of the ByLayer linetype (for defaults in MLeader etc.)
    bylayer_linetype_handle: Handle,
    /// Handle of the ByBlock linetype (treated as "unset" for MLeader etc.)
    byblock_linetype_handle: Handle,
    /// Handle of the *Model_Space block record — owner fallback for entities
    /// whose original owner was dropped during conversion (e.g. an application
    /// container object with no DXF representation).
    model_space_handle: Handle,
    /// Handle of the root named-objects dictionary — owner fallback for
    /// dictionaries whose owner was dropped.
    root_dict_handle: Handle,
}

impl<'a, W: DxfStreamWriter> SectionWriter<'a, W> {
    /// Create a new section writer
    pub fn new(writer: &'a mut W, handle_start: u64, handle_seed: u64) -> Self {
        Self {
            writer,
            next_handle: handle_start,
            handle_seed,
            dxf_version: DxfVersion::AC1024,
            sab_entries: Vec::new(),
            writing_paper_space: false,
            valid_handles: HashSet::new(),
            bylayer_linetype_handle: Handle::NULL,
            byblock_linetype_handle: Handle::NULL,
            model_space_handle: Handle::NULL,
            root_dict_handle: Handle::NULL,
        }
    }

    /// Build the set of all handles that will appear in the output DXF.
    /// Call this before writing BLOCKS / ENTITIES / OBJECTS.
    pub fn build_valid_handles(&mut self, document: &CadDocument) {
        let mut set = HashSet::new();
        // Object handles — but EXCLUDE unsupported objects read from DWG that
        // have no DXF representation (Unknown with no raw_dxf_codes):
        // write_unknown_object skips those, so any reference to them would
        // dangle and must be filtered out, or strict CAD readers reject the
        // file on audit.
        for (h, obj) in document.objects.iter() {
            if let ObjectType::Unknown { raw_dxf_codes, .. } = obj {
                if raw_dxf_codes.is_none() {
                    continue;
                }
            }
            set.insert(*h);
        }
        // Entity handles (from all block records); capture *Model_Space.
        for br in document.block_records.iter() {
            set.insert(br.handle());
            if br.name.eq_ignore_ascii_case("*Model_Space") {
                self.model_space_handle = br.handle();
            }
            for eh in &br.entity_handles {
                set.insert(*eh);
            }
        }
        // Entity index (covers all entities including orphans)
        for h in document.entity_index.keys() {
            set.insert(*h);
        }
        // Table record handles
        for r in document.layers.iter() { set.insert(r.handle()); }
        for r in document.line_types.iter() { set.insert(r.handle()); }
        for r in document.text_styles.iter() { set.insert(r.handle()); }
        for r in document.dim_styles.iter() { set.insert(r.handle()); }
        for r in document.app_ids.iter() { set.insert(r.handle()); }
        for r in document.views.iter() { set.insert(r.handle()); }
        for r in document.vports.iter() { set.insert(r.handle()); }
        for r in document.ucss.iter() { set.insert(r.handle()); }
        self.root_dict_handle = Self::find_root_dict_handle(&document.objects);
        self.valid_handles = set;
        // Store ByLayer/ByBlock linetype handles for use as default in MLeader etc.
        if let Some(lt) = document.line_types.get("ByLayer") {
            self.bylayer_linetype_handle = lt.handle();
        }
        if let Some(lt) = document.line_types.get("ByBlock") {
            self.byblock_linetype_handle = lt.handle();
        }
    }

    /// Set the target DXF version
    pub fn set_version(&mut self, version: DxfVersion) {
        self.dxf_version = version;
    }

    /// Returns true if the target version requires SAB binary format (AC1027+)
    fn needs_sab(&self) -> bool {
        self.dxf_version >= DxfVersion::AC1027
    }

    fn allocate_handle(&mut self) -> Handle {
        let handle = Handle::new(self.next_handle);
        self.next_handle += 1;
        handle
    }

    /// Write the HEADER section
    pub fn write_header(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("HEADER")?;
        let hdr = &document.header;

        // === Version & maintenance ===
        self.write_header_variable("$ACADVER", |w| {
            w.write_string(1, document.version.to_dxf_string())
        })?;
        self.write_header_variable("$ACADMAINTVER", |w| w.write_i16(70, 0))?;
        self.write_header_variable("$DWGCODEPAGE", |w| w.write_string(3, &hdr.code_page))?;

        let handle_seed = self.handle_seed;
        self.write_header_variable("$HANDSEED", |w| w.write_handle(5, Handle::new(handle_seed)))?;

        // === Drawing extents & limits ===
        self.write_header_variable("$INSBASE", |w| {
            let v = &hdr.model_space_insertion_base;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;
        self.write_header_variable("$EXTMIN", |w| {
            let v = &hdr.model_space_extents_min;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;
        self.write_header_variable("$EXTMAX", |w| {
            let v = &hdr.model_space_extents_max;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;
        self.write_header_variable("$LIMMIN", |w| {
            let v = &hdr.model_space_limits_min;
            w.write_double(10, v.x)?; w.write_double(20, v.y)
        })?;
        self.write_header_variable("$LIMMAX", |w| {
            let v = &hdr.model_space_limits_max;
            w.write_double(10, v.x)?; w.write_double(20, v.y)
        })?;

        // === Drawing modes ===
        self.write_header_variable("$ORTHOMODE", |w| w.write_i16(70, if hdr.ortho_mode { 1 } else { 0 }))?;
        self.write_header_variable("$REGENMODE", |w| w.write_i16(70, if hdr.regen_mode { 1 } else { 0 }))?;
        self.write_header_variable("$FILLMODE", |w| w.write_i16(70, if hdr.fill_mode { 1 } else { 0 }))?;
        self.write_header_variable("$QTEXTMODE", |w| w.write_i16(70, if hdr.quick_text_mode { 1 } else { 0 }))?;
        self.write_header_variable("$MIRRTEXT", |w| w.write_i16(70, if hdr.mirror_text { 1 } else { 0 }))?;
        self.write_header_variable("$LTSCALE", |w| w.write_double(40, hdr.linetype_scale))?;
        self.write_header_variable("$ATTMODE", |w| w.write_i16(70, hdr.attribute_visibility))?;
        self.write_header_variable("$TEXTSIZE", |w| w.write_double(40, hdr.text_height))?;
        self.write_header_variable("$TRACEWID", |w| w.write_double(40, hdr.trace_width))?;
        self.write_header_variable("$TEXTSTYLE", |w| w.write_string(7, &hdr.current_text_style_name))?;
        self.write_header_variable("$CMLSTYLE", |w| w.write_string(2, &hdr.multiline_style))?;
        self.write_header_variable("$CTABLESTYLE", |w| w.write_string(2, &hdr.current_table_style_name))?;
        self.write_header_variable("$CMLEADERSTYLE", |w| w.write_string(2, &hdr.current_mleader_style_name))?;
        self.write_header_variable("$CLAYER", |w| w.write_string(8, &hdr.current_layer_name))?;
        self.write_header_variable("$CELTYPE", |w| w.write_string(6, &hdr.current_linetype_name))?;
        self.write_header_variable("$CECOLOR", |w| w.write_i16(62, hdr.current_entity_color.approximate_index()))?;
        self.write_header_variable("$CELWEIGHT", |w| w.write_i16(370, hdr.current_line_weight))?;
        self.write_header_variable("$CELTSCALE", |w| w.write_double(40, hdr.current_entity_linetype_scale))?;
        self.write_header_variable("$DISPSILH", |w| w.write_i16(70, if hdr.display_silhouette { 1 } else { 0 }))?;

        // === Units ===
        self.write_header_variable("$LUNITS", |w| w.write_i16(70, hdr.linear_unit_format))?;
        self.write_header_variable("$LUPREC", |w| w.write_i16(70, hdr.linear_unit_precision))?;
        self.write_header_variable("$AUNITS", |w| w.write_i16(70, hdr.angular_unit_format))?;
        self.write_header_variable("$AUPREC", |w| w.write_i16(70, hdr.angular_unit_precision))?;
        self.write_header_variable("$MEASUREMENT", |w| w.write_i16(70, hdr.measurement))?;
        self.write_header_variable("$INSUNITS", |w| w.write_i16(70, hdr.insertion_units))?;

        // === Point display ===
        self.write_header_variable("$PDMODE", |w| w.write_i16(70, hdr.point_display_mode))?;
        self.write_header_variable("$PDSIZE", |w| w.write_double(40, hdr.point_display_size))?;
        self.write_header_variable("$PLINEGEN", |w| w.write_i16(70, if hdr.polyline_linetype_generation { 1 } else { 0 }))?;
        self.write_header_variable("$PSLTSCALE", |w| w.write_i16(70, if hdr.paper_space_linetype_scaling { 1 } else { 0 }))?;

        // === Dimension variables ===
        self.write_header_variable("$DIMSCALE", |w| w.write_double(40, hdr.dim_scale))?;
        self.write_header_variable("$DIMASZ", |w| w.write_double(40, hdr.dim_arrow_size))?;
        self.write_header_variable("$DIMEXO", |w| w.write_double(40, hdr.dim_ext_line_offset))?;
        self.write_header_variable("$DIMDLI", |w| w.write_double(40, hdr.dim_line_increment))?;
        self.write_header_variable("$DIMRND", |w| w.write_double(40, hdr.dim_rounding))?;
        self.write_header_variable("$DIMDLE", |w| w.write_double(40, hdr.dim_line_extension))?;
        self.write_header_variable("$DIMEXE", |w| w.write_double(40, hdr.dim_ext_line_extension))?;
        self.write_header_variable("$DIMTP", |w| w.write_double(40, hdr.dim_tolerance_plus))?;
        self.write_header_variable("$DIMTM", |w| w.write_double(40, hdr.dim_tolerance_minus))?;
        self.write_header_variable("$DIMTXT", |w| w.write_double(40, hdr.dim_text_height))?;
        self.write_header_variable("$DIMCEN", |w| w.write_double(40, hdr.dim_center_mark))?;
        self.write_header_variable("$DIMTSZ", |w| w.write_double(40, hdr.dim_tick_size))?;
        self.write_header_variable("$DIMTOL", |w| w.write_i16(70, if hdr.dim_tolerance { 1 } else { 0 }))?;
        self.write_header_variable("$DIMLIM", |w| w.write_i16(70, if hdr.dim_limits { 1 } else { 0 }))?;
        self.write_header_variable("$DIMTIH", |w| w.write_i16(70, if hdr.dim_text_inside_horizontal { 1 } else { 0 }))?;
        self.write_header_variable("$DIMTOH", |w| w.write_i16(70, if hdr.dim_text_outside_horizontal { 1 } else { 0 }))?;
        self.write_header_variable("$DIMSE1", |w| w.write_i16(70, if hdr.dim_suppress_ext1 { 1 } else { 0 }))?;
        self.write_header_variable("$DIMSE2", |w| w.write_i16(70, if hdr.dim_suppress_ext2 { 1 } else { 0 }))?;
        self.write_header_variable("$DIMTAD", |w| w.write_i16(70, hdr.dim_text_above))?;
        self.write_header_variable("$DIMZIN", |w| w.write_i16(70, hdr.dim_zero_suppression))?;
        self.write_header_variable("$DIMCLRD", |w| w.write_i16(70, hdr.dim_line_color.approximate_index()))?;
        self.write_header_variable("$DIMCLRE", |w| w.write_i16(70, hdr.dim_ext_line_color.approximate_index()))?;
        self.write_header_variable("$DIMCLRT", |w| w.write_i16(70, hdr.dim_text_color.approximate_index()))?;
        self.write_header_variable("$DIMGAP", |w| w.write_double(40, hdr.dim_line_gap))?;
        self.write_header_variable("$DIMALT", |w| w.write_i16(70, if hdr.dim_alternate_units { 1 } else { 0 }))?;
        self.write_header_variable("$DIMALTD", |w| w.write_i16(70, hdr.dim_alt_decimal_places))?;
        self.write_header_variable("$DIMALTF", |w| w.write_double(40, hdr.dim_alt_scale))?;
        self.write_header_variable("$DIMLFAC", |w| w.write_double(40, hdr.dim_linear_scale))?;
        self.write_header_variable("$DIMTOFL", |w| w.write_i16(70, if hdr.dim_force_line_inside { 1 } else { 0 }))?;
        self.write_header_variable("$DIMTVP", |w| w.write_double(40, hdr.dim_text_vertical_pos))?;
        self.write_header_variable("$DIMTIX", |w| w.write_i16(70, if hdr.dim_force_text_inside { 1 } else { 0 }))?;
        self.write_header_variable("$DIMSOXD", |w| w.write_i16(70, if hdr.dim_suppress_outside_ext { 1 } else { 0 }))?;
        self.write_header_variable("$DIMSAH", |w| w.write_i16(70, if hdr.dim_separate_arrows { 1 } else { 0 }))?;
        self.write_header_variable("$DIMPOST", |w| w.write_string(1, &hdr.dim_post))?;
        self.write_header_variable("$DIMAPOST", |w| w.write_string(1, &hdr.dim_alt_post))?;
        self.write_header_variable("$DIMSTYLE", |w| w.write_string(2, &hdr.current_dimstyle_name))?;
        self.write_header_variable("$DIMLUNIT", |w| w.write_i16(70, hdr.dim_linear_unit_format))?;
        self.write_header_variable("$DIMDEC", |w| w.write_i16(70, hdr.dim_decimal_places))?;
        self.write_header_variable("$DIMTDEC", |w| w.write_i16(70, hdr.dim_tolerance_decimal_places))?;
        self.write_header_variable("$DIMALTU", |w| w.write_i16(70, hdr.dim_alt_units_format))?;
        self.write_header_variable("$DIMALTTD", |w| w.write_i16(70, hdr.dim_alt_tolerance_decimal_places))?;
        self.write_header_variable("$DIMAUNIT", |w| w.write_i16(70, hdr.dim_angular_units))?;
        self.write_header_variable("$DIMADEC", |w| w.write_i16(70, hdr.dim_angular_decimal_places))?;
        self.write_header_variable("$DIMJUST", |w| w.write_i16(70, hdr.dim_horizontal_justification))?;
        self.write_header_variable("$DIMSD1", |w| w.write_i16(70, if hdr.dim_suppress_line1 { 1 } else { 0 }))?;
        self.write_header_variable("$DIMSD2", |w| w.write_i16(70, if hdr.dim_suppress_line2 { 1 } else { 0 }))?;
        self.write_header_variable("$DIMTOLJ", |w| w.write_i16(70, hdr.dim_tolerance_justification))?;
        self.write_header_variable("$DIMTZIN", |w| w.write_i16(70, hdr.dim_tolerance_zero_suppression))?;
        self.write_header_variable("$DIMALTZ", |w| w.write_i16(70, hdr.dim_alt_tolerance_zero_suppression))?;
        self.write_header_variable("$DIMALTTZ", |w| w.write_i16(70, hdr.dim_alt_tolerance_zero_tight))?;
        self.write_header_variable("$DIMATFIT", |w| w.write_i16(70, hdr.dim_fit))?;
        self.write_header_variable("$DIMDSEP", |w| w.write_i16(70, hdr.dim_decimal_separator as i16))?;
        self.write_header_variable("$DIMTMOVE", |w| w.write_i16(70, hdr.dim_text_movement))?;
        self.write_header_variable("$DIMFRAC", |w| w.write_i16(70, hdr.dim_fraction_format))?;
        self.write_header_variable("$DIMLWD", |w| w.write_i16(70, hdr.dim_line_weight))?;
        self.write_header_variable("$DIMLWE", |w| w.write_i16(70, hdr.dim_ext_line_weight))?;
        self.write_header_variable("$DIMTFAC", |w| w.write_double(40, hdr.dim_tolerance_scale))?;

        // === Misc ===
        self.write_header_variable("$SPLFRAME", |w| w.write_i16(70, if hdr.spline_frame { 1 } else { 0 }))?;
        self.write_header_variable("$SPLINETYPE", |w| w.write_i16(70, hdr.spline_type))?;
        self.write_header_variable("$SPLINESEGS", |w| w.write_i16(70, hdr.spline_segments))?;
        self.write_header_variable("$SURFTAB1", |w| w.write_i16(70, hdr.surface_tab1))?;
        self.write_header_variable("$SURFTAB2", |w| w.write_i16(70, hdr.surface_tab2))?;
        self.write_header_variable("$SURFTYPE", |w| w.write_i16(70, hdr.surface_type))?;
        self.write_header_variable("$SURFU", |w| w.write_i16(70, hdr.surface_u_density))?;
        self.write_header_variable("$SURFV", |w| w.write_i16(70, hdr.surface_v_density))?;
        self.write_header_variable("$WORLDVIEW", |w| w.write_i16(70, if hdr.world_view { 1 } else { 0 }))?;
        self.write_header_variable("$PELEVATION", |w| w.write_double(40, hdr.paper_elevation))?;
        self.write_header_variable("$PLINEWID", |w| w.write_double(40, hdr.polyline_width))?;
        self.write_header_variable("$MAXACTVP", |w| w.write_i16(70, hdr.max_active_viewports))?;
        self.write_header_variable("$TILEMODE", |w| w.write_i16(70, if hdr.show_model_space { 1 } else { 0 }))?;
        self.write_header_variable("$PLIMCHECK", |w| w.write_i16(70, if hdr.paper_space_limit_check { 1 } else { 0 }))?;
        self.write_header_variable("$VISRETAIN", |w| w.write_i16(70, if hdr.retain_xref_visibility { 1 } else { 0 }))?;

        // === Current annotation scale (R2008+) ===
        self.write_header_variable("$CANNOSCALE", |w| w.write_string(1, &hdr.current_annotation_scale))?;
        self.write_header_variable("$CANNOSCALEVALUE", |w| w.write_double(40, hdr.annotation_scale_value))?;

        // === Time ===
        self.write_header_variable("$TDCREATE", |w| w.write_double(40, hdr.create_date_julian))?;
        self.write_header_variable("$TDUPDATE", |w| w.write_double(40, hdr.update_date_julian))?;
        self.write_header_variable("$TDINDWG", |w| w.write_double(40, hdr.total_editing_time))?;

        // === UCS ===
        self.write_header_variable("$UCSORG", |w| {
            let v = &hdr.model_space_ucs_origin;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;
        self.write_header_variable("$UCSXDIR", |w| {
            let v = &hdr.model_space_ucs_x_axis;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;
        self.write_header_variable("$UCSYDIR", |w| {
            let v = &hdr.model_space_ucs_y_axis;
            w.write_double(10, v.x)?; w.write_double(20, v.y)?; w.write_double(30, v.z)
        })?;

        self.writer.write_section_end()?;
        Ok(())
    }

    /// Write a header variable
    fn write_header_variable<F>(&mut self, name: &str, write_value: F) -> Result<()>
    where
        F: FnOnce(&mut W) -> Result<()>,
    {
        self.writer.write_string(9, name)?;
        write_value(self.writer)
    }

    /// Write the CLASSES section
    pub fn write_classes(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("CLASSES")?;

        for class in document.classes.iter() {
            self.writer.write_string(0, "CLASS")?;
            self.writer.write_string(1, &class.dxf_name)?;
            self.writer.write_string(2, &class.cpp_class_name)?;
            self.writer.write_string(3, &class.application_name)?;
            self.writer.write_i32(90, class.proxy_flags.0 as i32)?;
            self.writer.write_i32(91, class.instance_count)?;
            self.writer.write_byte(280, if class.was_zombie { 1 } else { 0 })?;
            self.writer.write_byte(281, if class.is_an_entity { 1 } else { 0 })?;
        }

        self.writer.write_section_end()?;
        Ok(())
    }
    
    /// Write the TABLES section
    pub fn write_tables(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("TABLES")?;

        // Write tables in the standard order
        self.write_vport_table(document)?;
        self.write_ltype_table(document)?;
        self.write_layer_table(document)?;
        self.write_style_table(document)?;
        self.write_view_table(document)?;
        self.write_ucs_table(document)?;
        self.write_appid_table(document)?;
        self.write_dimstyle_table(document)?;
        self.write_block_record_table(document)?;

        self.writer.write_section_end()?;
        Ok(())
    }

    /// Write VPORT table
    fn write_vport_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.vports.handle();
        self.write_table_header("VPORT", document.vports.len(), table_handle)?;

        for vport in document.vports.iter() {
            self.write_vport_entry(vport, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_vport_entry(&mut self, vport: &VPort, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "VPORT")?;
        self.write_common_table_data(vport.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbViewportTableRecord")?;
        self.writer.write_string(2, vport.name())?;
        self.writer.write_i16(70, 0)?;

        // Lower-left corner
        self.writer.write_double(10, vport.lower_left.x)?;
        self.writer.write_double(20, vport.lower_left.y)?;

        // Upper-right corner
        self.writer.write_double(11, vport.upper_right.x)?;
        self.writer.write_double(21, vport.upper_right.y)?;

        // View center
        self.writer.write_double(12, vport.view_center.x)?;
        self.writer.write_double(22, vport.view_center.y)?;

        // Snap base point
        self.writer.write_double(13, vport.snap_base.x)?;
        self.writer.write_double(23, vport.snap_base.y)?;

        // Snap spacing
        self.writer.write_double(14, vport.snap_spacing.x)?;
        self.writer.write_double(24, vport.snap_spacing.y)?;

        // Grid spacing
        self.writer.write_double(15, vport.grid_spacing.x)?;
        self.writer.write_double(25, vport.grid_spacing.y)?;

        // View direction
        self.writer.write_double(16, vport.view_direction.x)?;
        self.writer.write_double(26, vport.view_direction.y)?;
        self.writer.write_double(36, vport.view_direction.z)?;

        // View target
        self.writer.write_double(17, vport.view_target.x)?;
        self.writer.write_double(27, vport.view_target.y)?;
        self.writer.write_double(37, vport.view_target.z)?;

        // View height
        self.writer.write_double(40, vport.view_height)?;

        // Aspect ratio
        self.writer.write_double(41, vport.aspect_ratio)?;

        // Lens length
        self.writer.write_double(42, vport.lens_length)?;

        // Front clipping plane
        self.writer.write_double(43, vport.front_clip)?;

        // Back clipping plane
        self.writer.write_double(44, vport.back_clip)?;

        // Snap rotation
        self.writer.write_double(50, vport.snap_rotation)?;

        // View twist angle
        self.writer.write_double(51, vport.view_twist)?;

        // View mode (bit flags: bit 2 = UCS follow)
        self.writer.write_i16(71, if vport.ucsfollow { 4 } else { 0 })?;

        // Circle zoom
        self.writer.write_i16(72, vport.circle_zoom)?;

        // Fast zoom
        self.writer.write_i16(73, if vport.fast_zoom { 1 } else { 0 })?;

        // UCSICON
        self.writer.write_i16(74, 3)?;

        // Snap on
        self.writer.write_i16(75, if vport.snap_on { 1 } else { 0 })?;

        // Grid on
        self.writer.write_i16(76, if vport.grid_on { 1 } else { 0 })?;

        // Snap style
        self.writer.write_i16(77, if vport.snap_style { 1 } else { 0 })?;

        // Snap isopair
        self.writer.write_i16(78, vport.snap_isopair)?;

        // Render mode / visual style
        self.writer.write_i16(281, vport.render_mode.to_value())?;

        Ok(())
    }

    /// Write LTYPE table
    fn write_ltype_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.line_types.handle();
        self.write_table_header("LTYPE", document.line_types.len(), table_handle)?;

        for ltype in document.line_types.iter() {
            self.write_ltype_entry(ltype, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_ltype_entry(&mut self, ltype: &LineType, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "LTYPE")?;
        self.write_common_table_data(ltype.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbLinetypeTableRecord")?;
        self.writer.write_string(2, ltype.name())?;
        let mut flags: i16 = 0;
        if ltype.xref_dependent {
            flags |= 0x10;
        }
        self.writer.write_i16(70, flags)?;
        self.writer.write_string(3, &ltype.description)?;
        self.writer.write_i16(72, 65)?; // Alignment code (always 65)
        self.writer.write_i16(73, ltype.elements.len() as i16)?;
        self.writer.write_double(40, ltype.pattern_length)?;

        for element in &ltype.elements {
            self.writer.write_double(49, element.length)?;
            if let Some(c) = &element.complex {
                // Build DXF element-type flags (code 75): 0x01=abs rot, 0x02=text, 0x04=shape
                let mut flags: i16 = 0;
                if c.is_absolute_rotation() { flags |= 0x01; }
                if c.is_shape() { flags |= 0x04; }
                else if c.is_text() { flags |= 0x02; }

                self.writer.write_i16(74, c.shape_number().unwrap_or(0))?;
                self.writer.write_i16(75, flags)?;
                if let Some(t) = c.text() {
                    if !t.is_empty() {
                        self.writer.write_string(9, t)?;
                    }
                }
                self.writer.write_double(44, c.offset[0])?;
                self.writer.write_double(45, c.offset[1])?;
                self.writer.write_double(46, c.scale)?;
                self.writer.write_double(50, c.rotation)?;
                if !c.style_handle.is_null() {
                    self.writer.write_handle(340, c.style_handle)?;
                }
            }
        }

        Ok(())
    }

    /// Write LAYER table
    fn write_layer_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.layers.handle();
        self.write_table_header("LAYER", document.layers.len(), table_handle)?;

        for layer in document.layers.iter() {
            self.write_layer_entry(layer, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_layer_entry(&mut self, layer: &Layer, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "LAYER")?;
        self.write_common_table_data(layer.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbLayerTableRecord")?;
        self.writer.write_string(2, layer.name())?;

        // Flags
        let mut flags: i16 = 0;
        if layer.is_frozen() {
            flags |= 1;
        }
        if layer.is_locked() {
            flags |= 4;
        }
        if layer.flags.xref_dependent {
            flags |= 0x10;
        }
        self.writer.write_i16(70, flags)?;

        // Color (negative if layer is off)
        let color_index = match layer.color {
            Color::Index(i) => i as i16,
            Color::ByLayer => 7,
            Color::ByBlock => 0,
            Color::Rgb { .. } => 7,
        };
        if !layer.is_off() {
            self.writer.write_i16(62, color_index)?;
        } else {
            self.writer.write_i16(62, -color_index)?;
        }

        // Linetype name
        self.writer.write_string(6, &layer.line_type)?;

        // Lineweight
        self.writer.write_i16(370, layer.line_weight.value())?;

        // Plot flag (code 290 is Bool type - single byte in binary)
        self.writer
            .write_bool(290, layer.is_plottable)?;

        Ok(())
    }

    /// Write STYLE table (text styles)
    fn write_style_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.text_styles.handle();
        self.write_table_header("STYLE", document.text_styles.len(), table_handle)?;

        for style in document.text_styles.iter() {
            self.write_style_entry(style, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    /// Persist the annotative flag the standard way: XDATA under the
    /// `AcadAnnotative` application, in the form
    /// `AnnotativeData { <version=1> <flag> }`. Written only when the record
    /// is annotative; its absence on read means non-annotative. This matches
    /// how AutoCAD stores annotative on STYLE / DIMSTYLE / TABLESTYLE records.
    fn write_annotative_xdata(&mut self, annotative: bool) -> Result<()> {
        if !annotative {
            return Ok(());
        }
        self.writer.write_string(1001, "AcadAnnotative")?;
        self.writer.write_string(1000, "AnnotativeData")?;
        self.writer.write_string(1002, "{")?;
        self.writer.write_i16(1070, 1)?;
        self.writer.write_i16(1070, 1)?;
        self.writer.write_string(1002, "}")?;
        Ok(())
    }

    fn write_style_entry(&mut self, style: &TextStyle, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "STYLE")?;
        self.write_common_table_data(style.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbTextStyleTableRecord")?;
        self.writer.write_string(2, style.name())?;
        let mut flags: i16 = 0;
        if style.xref_dependent { flags |= 0x10; }
        self.writer.write_i16(70, flags)?;
        self.writer.write_double(40, style.height)?;
        self.writer.write_double(41, style.width_factor)?;
        self.writer.write_double(50, style.oblique_angle)?;
        self.writer.write_i16(71, 0)?; // Text generation flags
        // Last height used — must be > 0 for CAD validation
        self.writer.write_double(42, style.effective_last_height())?;
        self.writer.write_string(3, &style.font_file)?;
        self.writer.write_string(4, &style.big_font_file)?;
        self.write_annotative_xdata(style.annotative)?;

        Ok(())
    }

    /// Write VIEW table
    fn write_view_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.views.handle();
        self.write_table_header("VIEW", document.views.len(), table_handle)?;

        for view in document.views.iter() {
            self.write_view_entry(view, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_view_entry(&mut self, view: &View, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "VIEW")?;
        self.write_common_table_data(view.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbViewTableRecord")?;
        self.writer.write_string(2, view.name())?;
        self.writer.write_i16(70, 0)?;
        self.writer.write_double(40, view.height)?;
        self.writer.write_double(10, view.center.x)?;
        self.writer.write_double(20, view.center.y)?;
        self.writer.write_double(41, view.width)?;
        self.writer.write_double(11, view.direction.x)?;
        self.writer.write_double(21, view.direction.y)?;
        self.writer.write_double(31, view.direction.z)?;
        self.writer.write_double(12, view.target.x)?;
        self.writer.write_double(22, view.target.y)?;
        self.writer.write_double(32, view.target.z)?;
        self.writer.write_double(42, view.lens_length)?;
        self.writer.write_double(43, view.front_clip)?;
        self.writer.write_double(44, view.back_clip)?;
        self.writer.write_double(50, view.twist_angle)?;

        Ok(())
    }

    /// Write UCS table
    fn write_ucs_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.ucss.handle();
        self.write_table_header("UCS", document.ucss.len(), table_handle)?;

        for ucs in document.ucss.iter() {
            self.write_ucs_entry(ucs, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_ucs_entry(&mut self, ucs: &Ucs, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "UCS")?;
        self.write_common_table_data(ucs.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbUCSTableRecord")?;
        self.writer.write_string(2, ucs.name())?;
        self.writer.write_i16(70, 0)?;
        self.writer.write_double(10, ucs.origin.x)?;
        self.writer.write_double(20, ucs.origin.y)?;
        self.writer.write_double(30, ucs.origin.z)?;
        self.writer.write_double(11, ucs.x_axis.x)?;
        self.writer.write_double(21, ucs.x_axis.y)?;
        self.writer.write_double(31, ucs.x_axis.z)?;
        self.writer.write_double(12, ucs.y_axis.x)?;
        self.writer.write_double(22, ucs.y_axis.y)?;
        self.writer.write_double(32, ucs.y_axis.z)?;

        Ok(())
    }

    /// Write APPID table
    fn write_appid_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.app_ids.handle();
        self.write_table_header("APPID", document.app_ids.len(), table_handle)?;

        for appid in document.app_ids.iter() {
            self.write_appid_entry(appid, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_appid_entry(&mut self, appid: &AppId, owner: Handle) -> Result<()> {
        // Sanitize name: strip control chars and characters forbidden in symbol table names
        let name = sanitize_symbol_name(appid.name());
        if name.is_empty() { return Ok(()); }
        self.writer.write_string(0, "APPID")?;
        self.write_common_table_data(appid.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbRegAppTableRecord")?;
        self.writer.write_string(2, &name)?;
        self.writer.write_i16(70, 0)?;

        Ok(())
    }

    /// Write DIMSTYLE table
    fn write_dimstyle_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.dim_styles.handle();
        self.write_table_header("DIMSTYLE", document.dim_styles.len(), table_handle)?;
        self.writer.write_subclass("AcDbDimStyleTable")?;
        self.writer.write_i16(71, document.dim_styles.len() as i16)?;

        for dimstyle in document.dim_styles.iter() {
            self.write_dimstyle_entry(dimstyle, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_dimstyle_entry(&mut self, dimstyle: &DimStyle, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "DIMSTYLE")?;
        self.writer.write_handle(105, dimstyle.handle())?;
        self.writer.write_handle(330, owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbDimStyleTableRecord")?;
        self.writer.write_string(2, dimstyle.name())?;
        self.writer.write_i16(70, 0)?;

        // Postfix / suffix
        if !dimstyle.dimpost.is_empty() && dimstyle.dimpost != "<>" { self.writer.write_string(3, &dimstyle.dimpost)?; }
        if !dimstyle.dimapost.is_empty() { self.writer.write_string(4, &dimstyle.dimapost)?; }

        // Scale / floats (codes 40-50)
        self.writer.write_double(40, dimstyle.dimscale)?;
        self.writer.write_double(41, dimstyle.dimasz)?;
        self.writer.write_double(42, dimstyle.dimexo)?;
        self.writer.write_double(43, dimstyle.dimdli)?;
        self.writer.write_double(44, dimstyle.dimexe)?;
        self.writer.write_double(45, dimstyle.dimrnd)?;
        self.writer.write_double(46, dimstyle.dimdle)?;
        self.writer.write_double(47, dimstyle.dimtp)?;
        self.writer.write_double(48, dimstyle.dimtm)?;
        if dimstyle.dimfxl != 1.0 { self.writer.write_double(49, dimstyle.dimfxl)?; }
        if dimstyle.dimjogang != std::f64::consts::FRAC_PI_4 {
            // Clamp to valid range [5°..90°]
            self.writer.write_double(50, dimstyle.dimjogang.clamp(0.0872665, 1.5708))?;
        }

        // Floats 140-148
        self.writer.write_double(140, dimstyle.dimtxt)?;
        self.writer.write_double(141, dimstyle.dimcen)?;
        self.writer.write_double(142, dimstyle.dimtsz)?;
        self.writer.write_double(143, dimstyle.dimaltf)?;
        self.writer.write_double(144, dimstyle.dimlfac)?;
        self.writer.write_double(145, dimstyle.dimtvp)?;
        self.writer.write_double(146, dimstyle.dimtfac)?;
        self.writer.write_double(147, dimstyle.dimgap)?;
        if dimstyle.dimaltrnd != 0.0 { self.writer.write_double(148, dimstyle.dimaltrnd)?; }

        // Int16 flags (69-79)
        if dimstyle.dimtfill != 0 { self.writer.write_i16(69, dimstyle.dimtfill)?; }
        self.writer.write_i16(71, if dimstyle.dimtol { 1 } else { 0 })?;
        self.writer.write_i16(72, if dimstyle.dimlim { 1 } else { 0 })?;
        self.writer.write_i16(73, if dimstyle.dimtih { 1 } else { 0 })?;
        self.writer.write_i16(74, if dimstyle.dimtoh { 1 } else { 0 })?;
        self.writer.write_i16(75, if dimstyle.dimse1 { 1 } else { 0 })?;
        self.writer.write_i16(76, if dimstyle.dimse2 { 1 } else { 0 })?;
        self.writer.write_i16(77, dimstyle.dimtad)?;
        self.writer.write_i16(78, dimstyle.dimzin)?;
        self.writer.write_i16(79, dimstyle.dimazin)?;

        // Int16 / Int32 (90, 170-179)
        if dimstyle.dimarcsym != 0 { self.writer.write_i32(90, dimstyle.dimarcsym as i32)?; }
        self.writer.write_i16(170, if dimstyle.dimalt { 1 } else { 0 })?;
        self.writer.write_i16(171, dimstyle.dimaltd)?;
        self.writer.write_i16(172, if dimstyle.dimtofl { 1 } else { 0 })?;
        self.writer.write_i16(173, if dimstyle.dimsah { 1 } else { 0 })?;
        self.writer.write_i16(174, if dimstyle.dimtix { 1 } else { 0 })?;
        self.writer.write_i16(175, if dimstyle.dimsoxd { 1 } else { 0 })?;
        self.writer.write_i16(176, dimstyle.dimclrd)?;
        self.writer.write_i16(177, dimstyle.dimclre)?;
        self.writer.write_i16(178, dimstyle.dimclrt)?;
        self.writer.write_i16(179, dimstyle.dimadec)?;

        // Int16 (270-290)
        self.writer.write_i16(271, dimstyle.dimdec)?;
        self.writer.write_i16(272, dimstyle.dimtdec)?;
        self.writer.write_i16(273, dimstyle.dimaltu)?;
        self.writer.write_i16(274, dimstyle.dimalttd)?;
        self.writer.write_i16(275, dimstyle.dimaunit)?;
        self.writer.write_i16(276, dimstyle.dimfrac)?;
        self.writer.write_i16(277, dimstyle.dimlunit)?;
        self.writer.write_i16(278, dimstyle.dimdsep)?;
        self.writer.write_i16(279, dimstyle.dimtmove)?;
        self.writer.write_i16(280, dimstyle.dimjust)?;
        self.writer.write_i16(281, if dimstyle.dimsd1 { 1 } else { 0 })?;
        self.writer.write_i16(282, if dimstyle.dimsd2 { 1 } else { 0 })?;
        self.writer.write_i16(283, dimstyle.dimtolj)?;
        self.writer.write_i16(284, dimstyle.dimtzin)?;
        self.writer.write_i16(285, dimstyle.dimaltz)?;
        self.writer.write_i16(286, dimstyle.dimalttz)?;
        self.writer.write_i16(289, dimstyle.dimatfit)?;
        if dimstyle.dimfxlon { self.writer.write_bool(290, true)?; }
        if dimstyle.dimtxtdirection { self.writer.write_bool(295, true)?; }

        // Handle references
        if !dimstyle.dimtxsty_handle.is_null() { self.writer.write_handle(340, dimstyle.dimtxsty_handle)?; }
        if !dimstyle.dimldrblk.is_null() { self.writer.write_handle(341, dimstyle.dimldrblk)?; }
        if !dimstyle.dimblk.is_null() { self.writer.write_handle(342, dimstyle.dimblk)?; }
        if !dimstyle.dimblk1.is_null() { self.writer.write_handle(343, dimstyle.dimblk1)?; }
        if !dimstyle.dimblk2.is_null() { self.writer.write_handle(344, dimstyle.dimblk2)?; }
        if !dimstyle.dimltex_handle.is_null() { self.writer.write_handle(345, dimstyle.dimltex_handle)?; }
        if !dimstyle.dimltex1_handle.is_null() { self.writer.write_handle(346, dimstyle.dimltex1_handle)?; }
        if !dimstyle.dimltex2_handle.is_null() { self.writer.write_handle(347, dimstyle.dimltex2_handle)?; }

        // Line weights
        self.writer.write_i16(371, dimstyle.dimlwd)?;
        self.writer.write_i16(372, dimstyle.dimlwe)?;
        self.write_annotative_xdata(dimstyle.annotative)?;

        Ok(())
    }

    /// Write BLOCK_RECORD table
    fn write_block_record_table(&mut self, document: &CadDocument) -> Result<()> {
        let table_handle = document.block_records.handle();
        self.write_table_header("BLOCK_RECORD", document.block_records.len(), table_handle)?;

        for block_record in document.block_records.iter() {
            self.write_block_record_entry(block_record, table_handle)?;
        }

        self.write_table_end()?;
        Ok(())
    }

    fn write_block_record_entry(&mut self, block_record: &BlockRecord, owner: Handle) -> Result<()> {
        self.writer.write_string(0, "BLOCK_RECORD")?;
        self.write_common_table_data(block_record.handle(), owner)?;
        self.writer.write_subclass("AcDbSymbolTableRecord")?;
        self.writer.write_subclass("AcDbBlockTableRecord")?;
        self.writer.write_string(2, block_record.name())?;
        self.writer.write_i16(70, block_record.units)?;
        self.writer
            .write_byte(280, if block_record.explodable { 1 } else { 0 })?;
        self.writer.write_i16(
            281,
            if block_record.scale_uniformly { 1 } else { 0 },
        )?;

        Ok(())
    }

    /// Write table header
    fn write_table_header(&mut self, name: &str, count: usize, table_handle: Handle) -> Result<()> {
        self.writer.write_string(0, "TABLE")?;
        self.writer.write_string(2, name)?;
        self.writer.write_handle(5, table_handle)?;
        self.writer.write_handle(330, Handle::new(0))?; // Tables owned by document root (handle 0)
        self.writer.write_subclass("AcDbSymbolTable")?;
        self.writer.write_i16(70, count as i16)?;
        Ok(())
    }

    /// Write table end
    fn write_table_end(&mut self) -> Result<()> {
        self.writer.write_string(0, "ENDTAB")
    }

    /// Write common table entry data
    fn write_common_table_data(&mut self, handle: Handle, owner: Handle) -> Result<()> {
        self.writer.write_handle(5, handle)?;
        self.writer.write_handle(330, owner)?;
        Ok(())
    }

    /// Write the BLOCKS section
    pub fn write_blocks(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("BLOCKS")?;

        for block_record in document.block_records.iter() {
            self.write_block_definition(block_record, document)?;
        }

        self.writer.write_section_end()?;
        Ok(())
    }

    /// Write a complete block definition (BLOCK...entities...ENDBLK)
    fn write_block_definition(&mut self, block_record: &BlockRecord, document: &CadDocument) -> Result<()> {
        let owner = block_record.handle();
        let is_paper_space = block_record.name().starts_with("*Paper_Space");
        
        // Determine block flags from stored BlockFlags
        let mut flags: i16 = 0;
        // Anonymous flag: use stored flag, or infer for truly anonymous blocks
        // (e.g. *D1, *U2, *T3, *X4, *E5, *A6) but NOT system blocks
        // (*Model_Space, *Paper_Space*)
        let name = block_record.name();
        let is_anonymous_block = block_record.flags.anonymous
            || (name.starts_with('*')
                && !name.starts_with("*Model_Space")
                && !name.starts_with("*Paper_Space"));
        if is_anonymous_block {
            flags |= 1; // anonymous
        }
        if block_record.flags.has_attributes {
            flags |= 2; // has attribute definitions
        }
        if block_record.flags.is_xref {
            flags |= 4; // xref
        }
        if block_record.flags.is_xref_overlay {
            flags |= 8; // xref overlay
        }
        if block_record.flags.is_external {
            flags |= 16; // externally dependent
        }
        
        // Write BLOCK entity
        self.writer.write_string(0, "BLOCK")?;
        self.writer.write_handle(5, block_record.block_entity_handle)?;
        self.writer.write_handle(330, owner)?;
        self.writer.write_subclass("AcDbEntity")?;
        // Paper space flag (group code 67) for all paper space blocks
        if is_paper_space {
            self.writer.write_i16(67, 1)?;
        }
        self.writer.write_string(8, "0")?;
        self.writer.write_subclass("AcDbBlockBegin")?;
        self.writer.write_string(2, block_record.name())?;
        self.writer.write_i16(70, flags)?;
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, 0.0)?;
        self.writer.write_string(3, block_record.name())?;
        // Group code 1 is XRef path (empty for normal blocks)
        self.writer.write_string(1, &block_record.xref_path)?;

        // Write entities inside block definition:
        // - Model space entities go to ENTITIES section (not here)
        // - Active paper space (*Paper_Space) entities go to ENTITIES section (not here)
        // - Non-active paper spaces (*Paper_Space0, *Paper_Space1, ...) write entities here
        // - Other blocks (inserts etc.) also write entities here
        if !block_record.is_model_space() && block_record.name() != "*Paper_Space" {
            // Set paper space flag so entities inside non-active paper
            // space blocks get code 67=1 (same as active paper space).
            let prev_ps = self.writing_paper_space;
            if is_paper_space {
                self.writing_paper_space = true;
            }
            for eh in &block_record.entity_handles {
                if let Some(&idx) = document.entity_index.get(eh) {
                    self.write_entity_with_owner(&document.entities[idx], owner)?;
                }
            }
            self.writing_paper_space = prev_ps;
        }

        // Write ENDBLK entity
        self.writer.write_string(0, "ENDBLK")?;
        self.writer.write_handle(5, block_record.block_end_handle)?;
        self.writer.write_handle(330, owner)?;
        self.writer.write_subclass("AcDbEntity")?;
        // Paper space flag for ENDBLK too
        if is_paper_space {
            self.writer.write_i16(67, 1)?;
        }
        self.writer.write_string(8, "0")?;
        self.writer.write_subclass("AcDbBlockEnd")?;

        Ok(())
    }

    /// Write the ENTITIES section
    pub fn write_entities(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("ENTITIES")?;

        // Write entities from model space block record
        self.writing_paper_space = false;
        if let Some(model_space) = document.block_records.get("*Model_Space") {
            let owner = model_space.handle();
            for eh in &model_space.entity_handles {
                if let Some(&idx) = document.entity_index.get(eh) {
                    self.write_entity_with_owner(&document.entities[idx], owner)?;
                }
            }
        }

        // Write entities from the active paper space (*Paper_Space) only.
        // Non-active paper spaces (*Paper_Space0, *Paper_Space1, ...) have their
        // entities written inside their BLOCK definitions in the BLOCKS section.
        self.writing_paper_space = true;
        if let Some(paper_space) = document.block_records.get("*Paper_Space") {
            let owner = paper_space.handle();
            for eh in &paper_space.entity_handles {
                if let Some(&idx) = document.entity_index.get(eh) {
                    self.write_entity_with_owner(&document.entities[idx], owner)?;
                }
            }
        }
        self.writing_paper_space = false;

        self.writer.write_section_end()?;
        Ok(())
    }

    /// Write an entity with explicit owner
    fn write_entity_with_owner(&mut self, entity: &EntityType, owner: Handle) -> Result<()> {
        match entity {
            EntityType::Point(e) => self.write_point(e, owner),
            EntityType::Line(e) => self.write_line(e, owner),
            EntityType::Circle(e) => self.write_circle(e, owner),
            EntityType::Arc(e) => self.write_arc(e, owner),
            EntityType::Ellipse(e) => self.write_ellipse(e, owner),
            EntityType::Polyline(e) => self.write_polyline(e, owner),
            EntityType::Polyline2D(e) => self.write_polyline2d(e, owner),
            EntityType::Polyline3D(e) => self.write_polyline3d(e, owner),
            EntityType::LwPolyline(e) => self.write_lwpolyline(e, owner),
            EntityType::Text(e) => self.write_text(e, owner),
            EntityType::MText(e) => self.write_mtext(e, owner),
            EntityType::Spline(e) => self.write_spline(e, owner),
            EntityType::Helix(e) => self.write_helix(e, owner),
            EntityType::Dimension(dim) => self.write_dimension(dim, owner),
            EntityType::Hatch(e) => self.write_hatch(e, owner),
            EntityType::Solid(e) => self.write_solid(e, owner),
            EntityType::Face3D(e) => self.write_face3d(e, owner),
            EntityType::Insert(e) => self.write_insert(e, owner),
            EntityType::Block(e) => self.write_block_entity(e, owner),
            EntityType::BlockEnd(e) => self.write_block_end(e, owner),
            EntityType::Ray(e) => self.write_ray(e, owner),
            EntityType::XLine(e) => self.write_xline(e, owner),
            EntityType::Viewport(e) => self.write_viewport(e, owner),
            EntityType::AttributeDefinition(e) => self.write_attdef(e, owner),
            EntityType::AttributeEntity(e) => self.write_attrib(e, owner),
            EntityType::Leader(e) => self.write_leader(e, owner),
            EntityType::MultiLeader(e) => self.write_multileader(e, owner),
            EntityType::MLine(e) => self.write_mline(e, owner),
            EntityType::Mesh(e) => self.write_mesh(e, owner),
            EntityType::RasterImage(e) => self.write_raster_image(e, owner),
            EntityType::Solid3D(e) => self.write_solid3d(e, owner),
            EntityType::Region(e) => self.write_region(e, owner),
            EntityType::Body(e) => self.write_body(e, owner),
            // DXF surface export not yet supported; skip rather than emit
            // malformed geometry.
            EntityType::Surface(_) => Ok(()),
            EntityType::Table(e) => self.write_acad_table(e, owner),
            EntityType::Tolerance(e) => self.write_tolerance(e, owner),
            EntityType::PolyfaceMesh(e) => self.write_polyface_mesh(e, owner),
            EntityType::Wipeout(e) => self.write_wipeout(e, owner),
            EntityType::Shape(e) => self.write_shape(e, owner),
            EntityType::Underlay(e) => self.write_underlay(e, owner),
            EntityType::Seqend(e) => self.write_seqend(e, owner),
            EntityType::Ole2Frame(e) => self.write_ole2frame(e, owner),
            EntityType::PolygonMesh(e) => self.write_polygon_mesh(e, owner),
            EntityType::Unknown(e) => self.write_unknown_entity(e, owner),
        }
    }

    /// Return `owner` if it will be present in the output, else fall back to the
    /// *Model_Space record. An entity whose original owner (e.g. a dropped
    /// application container with no DXF form) is gone would otherwise emit a
    /// dangling 330 reference that strict CAD readers reject on audit.
    fn safe_entity_owner(&self, owner: Handle) -> Handle {
        if owner == Handle::NULL
            || self.valid_handles.is_empty()
            || self.valid_handles.contains(&owner)
            || self.model_space_handle == Handle::NULL
        {
            owner
        } else {
            self.model_space_handle
        }
    }

    /// Write common entity data with owner
    fn write_common_entity_data(&mut self, common: &EntityCommon, owner: Handle) -> Result<()> {
        let owner = self.safe_entity_owner(owner);
        self.writer.write_handle(5, common.handle)?;
        self.writer.write_handle(330, owner)?;

        // Write xdictionary group
        if let Some(xdict) = common.xdictionary_handle {
            if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
                self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
                self.writer.write_handle(360, xdict)?;
                self.writer.write_string(102, "}")?;
            }
        }

        // Write reactor group (filter out reactors pointing to non-existent objects)
        if !common.reactors.is_empty() {
            let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
                common.reactors.clone()
            } else {
                common.reactors.iter().copied()
                    .filter(|r| self.valid_handles.contains(r))
                    .collect()
            };
            if !valid_reactors.is_empty() {
                self.writer.write_string(102, "{ACAD_REACTORS")?;
                for reactor in &valid_reactors {
                    self.writer.write_handle(330, *reactor)?;
                }
                self.writer.write_string(102, "}")?;
            }
        }

        self.writer.write_subclass("AcDbEntity")?;

        // Paper space flag (code 67) — required for entities in paper space
        if self.writing_paper_space {
            self.writer.write_i16(67, 1)?;
        }

        self.writer.write_string(8, &common.layer)?;

        // Write linetype if not default (ByLayer)
        if common.has_linetype() {
            self.writer.write_string(6, &common.linetype)?;
        }

        // Write color only if not ByLayer (default)
        if common.color != Color::ByLayer {
            self.writer.write_color(62, common.color)?;
        }

        // True color (code 420) — only for AC1018+ (AutoCAD 2004+)
        if self.dxf_version >= DxfVersion::AC1018 {
            if let Some(tc) = common.color.to_true_color_value() {
                self.writer.write_i32(420, tc)?;
            }
        }

        // Write linetype scale if not 1.0
        if (common.linetype_scale - 1.0).abs() > 1e-12 {
            self.writer.write_double(48, common.linetype_scale)?;
        }

        // Write lineweight if not default
        if common.line_weight != crate::types::LineWeight::ByLayer {
            self.writer.write_i16(370, common.line_weight.value())?;
        }

        // Write visibility
        if common.invisible {
            self.writer.write_i16(60, 1)?;
        }

        // Transparency (code 440) — only for AC1018+ and non-opaque
        if self.dxf_version >= DxfVersion::AC1018 && !common.transparency.is_opaque() {
            self.writer.write_i32(440, common.transparency.to_dxf_value())?;
        }

        Ok(())
    }

    /// Write extrusion direction (normal vector, codes 210/220/230) if not default (0,0,1).
    fn write_normal(&mut self, normal: Vector3) -> Result<()> {
        if normal != Vector3::UNIT_Z {
            self.writer.write_double(210, normal.x)?;
            self.writer.write_double(220, normal.y)?;
            self.writer.write_double(230, normal.z)?;
        }
        Ok(())
    }

    /// Write an unknown entity, preserving raw group codes if available.
    fn write_unknown_entity(
        &mut self,
        entity: &crate::entities::UnknownEntity,
        owner: Handle,
    ) -> Result<()> {
        if let Some(ref codes) = entity.raw_dxf_codes {
            // Write the original DXF type name (e.g. "ACAD_PROXY_ENTITY")
            self.writer.write_entity_type(&entity.dxf_name)?;
            self.write_common_entity_data(&entity.common, owner)?;
            // Write preserved entity-specific codes
            for (code, value) in codes {
                self.writer.write_string(*code, value)?;
            }
            Ok(())
        } else {
            // No raw data — skip this entity
            Ok(())
        }
    }

    /// Write POINT entity
    fn write_point(&mut self, point: &Point, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("POINT")?;
        self.write_common_entity_data(&point.common, owner)?;
        self.writer.write_subclass("AcDbPoint")?;
        self.writer.write_point3d(10, point.location)?;
        if point.thickness != 0.0 {
            self.writer.write_double(39, point.thickness)?;
        }
        self.write_normal(point.normal)?;
        if point.x_axis_angle != 0.0 {
            self.writer.write_double(50, point.x_axis_angle)?;
        }
        Ok(())
    }

    /// Write LINE entity
    fn write_line(&mut self, line: &Line, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("LINE")?;
        self.write_common_entity_data(&line.common, owner)?;
        self.writer.write_subclass("AcDbLine")?;
        self.writer.write_point3d(10, line.start)?;
        self.writer.write_point3d(11, line.end)?;
        if line.thickness != 0.0 {
            self.writer.write_double(39, line.thickness)?;
        }
        self.write_normal(line.normal)?;
        Ok(())
    }

    /// Write CIRCLE entity
    fn write_circle(&mut self, circle: &Circle, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("CIRCLE")?;
        self.write_common_entity_data(&circle.common, owner)?;
        self.writer.write_subclass("AcDbCircle")?;
        self.writer.write_point3d(10, circle.center)?;
        self.writer.write_double(40, circle.radius)?;
        if circle.thickness != 0.0 {
            self.writer.write_double(39, circle.thickness)?;
        }
        self.write_normal(circle.normal)?;
        Ok(())
    }

    /// Write ARC entity
    fn write_arc(&mut self, arc: &Arc, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ARC")?;
        self.write_common_entity_data(&arc.common, owner)?;
        self.writer.write_subclass("AcDbCircle")?;
        self.writer.write_point3d(10, arc.center)?;
        self.writer.write_double(40, arc.radius)?;
        if arc.thickness != 0.0 {
            self.writer.write_double(39, arc.thickness)?;
        }
        self.write_normal(arc.normal)?;
        self.writer.write_subclass("AcDbArc")?;
        self.writer.write_double(50, arc.start_angle.to_degrees())?;
        self.writer.write_double(51, arc.end_angle.to_degrees())?;
        Ok(())
    }

    /// Write ELLIPSE entity
    fn write_ellipse(&mut self, ellipse: &Ellipse, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ELLIPSE")?;
        self.write_common_entity_data(&ellipse.common, owner)?;
        self.writer.write_subclass("AcDbEllipse")?;
        self.writer.write_point3d(10, ellipse.center)?;
        self.writer.write_point3d(11, ellipse.major_axis)?;
        self.writer.write_double(40, ellipse.minor_axis_ratio)?;
        self.write_normal(ellipse.normal)?;
        self.writer.write_double(41, ellipse.start_parameter)?;
        self.writer.write_double(42, ellipse.end_parameter)?;
        Ok(())
    }

    /// Write POLYLINE entity (3D polyline)
    fn write_polyline(&mut self, polyline: &Polyline, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("POLYLINE")?;
        self.write_common_entity_data(&polyline.common, owner)?;
        self.writer.write_subclass("AcDb3dPolyline")?;

        // Entities follow flag (VERTEX records follow)
        self.writer.write_i16(66, 1)?;

        // Dummy point (required by DXF spec)
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, 0.0)?;

        let mut flags: i16 = 8; // 3D polyline flag
        if polyline.is_closed() {
            flags |= 1;
        }
        self.writer.write_i16(70, flags)?;

        // VERTEX and SEQEND are owned by the polyline entity
        let polyline_handle = polyline.common.handle;

        // Write vertices with proper subclass markers
        for vertex in polyline.vertices.iter() {
            let vertex_handle = self.allocate_handle();
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, vertex_handle)?;
            self.writer.write_handle(330, polyline_handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &polyline.common.layer)?;
            // Propagate parent color to vertex so CAD doesn't flag mismatch
            if polyline.common.color != Color::ByLayer {
                self.writer.write_color(62, polyline.common.color)?;
            }
            self.writer.write_subclass("AcDbVertex")?;
            self.writer.write_subclass("AcDb3dPolylineVertex")?;
            self.writer.write_point3d(10, vertex.location)?;
            self.writer.write_i16(70, 32)?; // 3D polyline vertex
        }

        // Write SEQEND
        let seqend_handle = self.allocate_handle();
        self.writer.write_entity_type("SEQEND")?;
        self.writer.write_handle(5, seqend_handle)?;
        self.writer.write_handle(330, polyline_handle)?;
        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &polyline.common.layer)?;

        Ok(())
    }
    
    /// Write POLYLINE entity (2D polyline)
    fn write_polyline2d(&mut self, polyline: &Polyline2D, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("POLYLINE")?;
        self.write_common_entity_data(&polyline.common, owner)?;
        self.writer.write_subclass("AcDb2dPolyline")?;

        // Entities follow flag (VERTEX records follow)
        self.writer.write_i16(66, 1)?;

        // Dummy origin point (required by DXF spec for POLYLINE entity)
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, polyline.elevation)?;

        self.writer.write_i16(70, polyline.flags.bits() as i16)?;

        if polyline.thickness != 0.0 {
            self.writer.write_double(39, polyline.thickness)?;
        }
        if polyline.start_width != 0.0 {
            self.writer.write_double(40, polyline.start_width)?;
        }
        if polyline.end_width != 0.0 {
            self.writer.write_double(41, polyline.end_width)?;
        }

        // VERTEX and SEQEND are owned by the polyline entity
        let polyline_handle = polyline.common.handle;

        // Write vertices with proper subclass markers
        for vertex in polyline.vertices.iter() {
            let vertex_handle = self.allocate_handle();
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, vertex_handle)?;
            self.writer.write_handle(330, polyline_handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &polyline.common.layer)?;
            // Propagate parent color to vertex so CAD doesn't flag mismatch
            if polyline.common.color != Color::ByLayer {
                self.writer.write_color(62, polyline.common.color)?;
            }
            self.writer.write_subclass("AcDbVertex")?;
            self.writer.write_subclass("AcDb2dVertex")?;
            self.writer.write_point3d(10, vertex.location)?;
            if vertex.start_width != 0.0 {
                self.writer.write_double(40, vertex.start_width)?;
            }
            if vertex.end_width != 0.0 {
                self.writer.write_double(41, vertex.end_width)?;
            }
            if vertex.bulge != 0.0 {
                self.writer.write_double(42, vertex.bulge)?;
            }
            self.writer.write_i16(70, vertex.flags.bits() as i16)?;
        }

        // Write SEQEND
        let seqend_handle = self.allocate_handle();
        self.writer.write_entity_type("SEQEND")?;
        self.writer.write_handle(5, seqend_handle)?;
        self.writer.write_handle(330, polyline_handle)?;
        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &polyline.common.layer)?;

        Ok(())
    }

    /// Write LWPOLYLINE entity
    fn write_lwpolyline(&mut self, lwpoly: &LwPolyline, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("LWPOLYLINE")?;
        self.write_common_entity_data(&lwpoly.common, owner)?;
        self.writer.write_subclass("AcDbPolyline")?;
        self.writer.write_i32(90, lwpoly.vertices.len() as i32)?;

        let mut flags: i16 = 0;
        if lwpoly.is_closed {
            flags |= 1;
        }
        if lwpoly.plinegen {
            flags |= 128;
        }
        self.writer.write_i16(70, flags)?;

        self.writer.write_double(38, lwpoly.elevation)?;
        if lwpoly.thickness != 0.0 {
            self.writer.write_double(39, lwpoly.thickness)?;
        }
        if lwpoly.constant_width != 0.0 {
            self.writer.write_double(43, lwpoly.constant_width)?;
        }

        for vertex in &lwpoly.vertices {
            self.writer.write_double(10, vertex.location.x)?;
            self.writer.write_double(20, vertex.location.y)?;
            // Always write start width, end width, and bulge (default to 0.0 if not set)
            self.writer.write_double(40, vertex.start_width)?;
            self.writer.write_double(41, vertex.end_width)?;
            self.writer.write_double(42, vertex.bulge)?;
        }

        self.write_normal(lwpoly.normal)?;
        Ok(())
    }

    /// Write TEXT entity
    fn write_text(&mut self, text: &Text, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("TEXT")?;
        self.write_common_entity_data(&text.common, owner)?;
        self.writer.write_subclass("AcDbText")?;
        if text.thickness != 0.0 {
            self.writer.write_double(39, text.thickness)?;
        }
        self.writer.write_point3d(10, text.insertion_point)?;
        self.writer.write_double(40, text.height)?;
        self.writer.write_string(1, &text.value)?;
        if text.rotation != 0.0 {
            self.writer.write_double(50, text.rotation.to_degrees())?;
        }
        if text.width_factor != 1.0 {
            self.writer.write_double(41, text.width_factor)?;
        }
        if text.oblique_angle != 0.0 {
            self.writer.write_double(51, text.oblique_angle)?;
        }
        self.writer.write_string(7, &text.style)?;
        if text.generation_flags != 0 {
            self.writer.write_i16(71, text.generation_flags)?;
        }
        self.writer.write_i16(72, text.horizontal_alignment as i16)?;
        if let Some(align_pt) = text.alignment_point {
            self.writer.write_point3d(11, align_pt)?;
        }
        self.write_normal(text.normal)?;
        self.writer.write_subclass("AcDbText")?;
        self.writer.write_i16(73, text.vertical_alignment as i16)?;
        Ok(())
    }

    /// Write MTEXT entity
    fn write_mtext(&mut self, mtext: &MText, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("MTEXT")?;
        self.write_common_entity_data(&mtext.common, owner)?;
        self.writer.write_subclass("AcDbMText")?;
        self.writer.write_point3d(10, mtext.insertion_point)?;
        self.writer.write_double(40, mtext.height)?;
        self.writer.write_double(41, mtext.rectangle_width)?;
        self.writer.write_i16(71, mtext.attachment_point as i16)?;
        self.writer.write_i16(72, mtext.drawing_direction as i16)?;

        // Write text value (may need to be split for long text).
        // DXF text format is line-based, so literal \n / \r in the value would
        // corrupt the file.  Replace them with the MText paragraph mark \P.
        let sanitized;
        let text: &str = if mtext.value.contains('\n') || mtext.value.contains('\r') {
            sanitized = mtext.value.replace("\r\n", "\\P").replace('\r', "\\P").replace('\n', "\\P");
            &sanitized
        } else {
            &mtext.value
        };
        if text.len() > 250 {
            // Split into chunks at char boundaries
            let mut remaining = text;
            while remaining.len() > 250 {
                // Find a valid char boundary at or before byte 250
                let mut split_pos = 250;
                while split_pos > 0 && !remaining.is_char_boundary(split_pos) {
                    split_pos -= 1;
                }
                if split_pos == 0 {
                    split_pos = remaining.len();
                }
                let (chunk, rest) = remaining.split_at(split_pos);
                self.writer.write_string(3, chunk)?;
                remaining = rest;
            }
            self.writer.write_string(1, remaining)?;
        } else {
            self.writer.write_string(1, text)?;
        }

        self.writer.write_string(7, &mtext.style)?;
        if mtext.rotation != 0.0 {
            self.writer.write_double(50, mtext.rotation.to_degrees())?;
        }
        self.writer.write_i16(73, mtext.line_spacing_style as i16)?;
        self.writer.write_double(44, mtext.line_spacing_factor)?;
        if let Some(h) = mtext.rectangle_height {
            self.writer.write_double(46, h)?;
        }
        // Background fill — only when enabled by the flags.
        if mtext.background_fill_flags != 0 {
            self.writer.write_i32(90, mtext.background_fill_flags)?;
            self.writer.write_double(45, mtext.background_scale)?;
            if let Some(tc) = mtext.background_color.to_true_color_value() {
                self.writer.write_i32(421, tc)?;
            } else if let Some(idx) = mtext.background_color.index() {
                self.writer.write_i16(63, idx as i16)?;
            }
            if mtext.background_transparency != 0 {
                self.writer.write_i32(441, mtext.background_transparency)?;
            }
        }
        self.write_normal(mtext.normal)?;
        Ok(())
    }

    /// Write SPLINE entity
    fn write_spline(&mut self, spline: &Spline, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("SPLINE")?;
        self.write_common_entity_data(&spline.common, owner)?;
        self.write_spline_body(spline)?;
        Ok(())
    }

    /// Write the AcDbSpline subclass block (marker + all spline group codes).
    /// Shared by SPLINE and HELIX, which carries a spline as its geometry.
    fn write_spline_body(&mut self, spline: &Spline) -> Result<()> {
        self.writer.write_subclass("AcDbSpline")?;

        // Normal vector
        self.write_normal(spline.normal)?;

        // Flags
        let mut flags: i16 = 0;
        if spline.flags.closed {
            flags |= 1;
        }
        if spline.flags.periodic {
            flags |= 2;
        }
        if spline.flags.rational {
            flags |= 4;
        }
        if spline.flags.planar {
            flags |= 8;
        }
        if spline.flags.linear {
            flags |= 16;
        }
        self.writer.write_i16(70, flags)?;

        self.writer.write_i16(71, spline.degree as i16)?;
        self.writer.write_i16(72, spline.knots.len() as i16)?;
        self.writer
            .write_i16(73, spline.control_points.len() as i16)?;
        self.writer.write_i16(74, spline.fit_points.len() as i16)?;

        // Knot / control-point / fit tolerances (round-trip the stored values).
        self.writer.write_double(42, spline.knot_tolerance)?;
        self.writer.write_double(43, spline.control_tolerance)?;
        self.writer.write_double(44, spline.fit_tolerance)?;

        // Start / end tangents (only when set).
        if spline.begin_tangent != Vector3::ZERO {
            self.writer.write_point3d(12, spline.begin_tangent)?;
        }
        if spline.end_tangent != Vector3::ZERO {
            self.writer.write_point3d(13, spline.end_tangent)?;
        }

        // Knots
        for knot in &spline.knots {
            self.writer.write_double(40, *knot)?;
        }

        // Control points (with optional weights for rational splines)
        for (i, point) in spline.control_points.iter().enumerate() {
            self.writer.write_point3d(10, *point)?;
            if spline.flags.rational {
                let w = spline.weights.get(i).copied().unwrap_or(1.0);
                self.writer.write_double(41, w)?;
            }
        }

        // Fit points
        for point in &spline.fit_points {
            self.writer.write_point3d(11, *point)?;
        }

        Ok(())
    }

    /// Write a HELIX entity: the AcDbSpline geometry block followed by the
    /// AcDbHelix parameters.
    fn write_helix(&mut self, helix: &Helix, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("HELIX")?;
        self.write_common_entity_data(&helix.common, owner)?;
        self.write_spline_body(&helix.spline)?;

        self.writer.write_subclass("AcDbHelix")?;
        self.writer.write_i32(90, helix.major_version)?;
        self.writer.write_i32(91, helix.maintenance_version)?;
        self.writer.write_point3d(10, helix.axis_base_point)?;
        self.writer.write_point3d(11, helix.start_point)?;
        self.writer.write_point3d(12, helix.axis_vector)?;
        self.writer.write_double(40, helix.radius)?;
        self.writer.write_double(41, helix.turns)?;
        self.writer.write_double(42, helix.turn_height)?;
        self.writer.write_bool(290, helix.handedness)?;
        self.writer.write_byte(280, helix.constraint.to_code())?;
        Ok(())
    }

    /// Write DIMENSION entity
    fn write_dimension(&mut self, dimension: &Dimension, owner: Handle) -> Result<()> {
        match dimension {
            Dimension::Aligned(dim) => self.write_dimension_aligned(dim, owner),
            Dimension::Linear(dim) => self.write_dimension_linear(dim, owner),
            Dimension::Radius(dim) => self.write_dimension_radius(dim, owner),
            Dimension::Diameter(dim) => self.write_dimension_diameter(dim, owner),
            Dimension::Angular2Ln(dim) => self.write_dimension_angular_2line(dim, owner),
            Dimension::Angular3Pt(dim) => self.write_dimension_angular_3point(dim, owner),
            Dimension::Ordinate(dim) => self.write_dimension_ordinate(dim, owner),
        }
    }

    fn write_dimension_base(&mut self, base: &DimensionBase, type_flags: i16, owner: Handle) -> Result<()> {
        self.writer.write_handle(5, base.common.handle)?;
        self.writer.write_handle(330, owner)?;

        // Write xdictionary group
        if let Some(xdict) = base.common.xdictionary_handle {
            if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
                self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
                self.writer.write_handle(360, xdict)?;
                self.writer.write_string(102, "}")?;
            }
        }

        // Write reactor group (filter out reactors pointing to non-existent objects)
        if !base.common.reactors.is_empty() {
            let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
                base.common.reactors.clone()
            } else {
                base.common.reactors.iter().copied()
                    .filter(|r| self.valid_handles.contains(r))
                    .collect()
            };
            if !valid_reactors.is_empty() {
                self.writer.write_string(102, "{ACAD_REACTORS")?;
                for reactor in &valid_reactors {
                    self.writer.write_handle(330, *reactor)?;
                }
                self.writer.write_string(102, "}")?;
            }
        }

        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &base.common.layer)?;

        // Write color if not ByLayer
        if base.common.color != Color::ByLayer {
            self.writer.write_color(62, base.common.color)?;
        }
        // True color (code 420) — only for AC1018+
        if self.dxf_version >= DxfVersion::AC1018 {
            if let Some(tc) = base.common.color.to_true_color_value() {
                self.writer.write_i32(420, tc)?;
            }
        }
        // Transparency (code 440) — only for AC1018+ and non-opaque
        if self.dxf_version >= DxfVersion::AC1018 && !base.common.transparency.is_opaque() {
            self.writer.write_i32(440, base.common.transparency.to_dxf_value())?;
        }

        self.writer.write_subclass("AcDbDimension")?;
        self.writer.write_string(2, &base.block_name)?;
        self.writer.write_point3d(10, base.definition_point)?;
        self.writer.write_point3d(11, base.text_middle_point)?;
        // Bit 0x80 marks text positioned at a user-defined location.
        let type_flags = if base.text_user_positioned {
            type_flags | 0x80
        } else {
            type_flags
        };
        self.writer.write_i16(70, type_flags)?;
        // DXF angles are in degrees; internal representation is radians.
        self.writer.write_double(53, base.text_rotation.to_degrees())?;
        self.writer.write_string(3, &base.style_name)?;
        if !base.text.is_empty() {
            self.writer.write_string(1, &base.text)?;
        }
        if (base.line_spacing_factor - 1.0).abs() > 1e-10 {
            self.writer.write_double(44, base.line_spacing_factor)?;
        }
        // Normal vector (extrusion direction) — only write if not default (0,0,1)
        let n = base.normal;
        if (n.x).abs() > 1e-12 || (n.y).abs() > 1e-12 || (n.z - 1.0).abs() > 1e-12 {
            self.writer.write_double(210, n.x)?;
            self.writer.write_double(220, n.y)?;
            self.writer.write_double(230, n.z)?;
        }
        Ok(())
    }

    fn write_dimension_aligned(&mut self, dim: &DimensionAligned, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 1, owner)?; // Aligned = 1
        self.writer.write_subclass("AcDbAlignedDimension")?;
        self.writer.write_point3d(13, dim.first_point)?;
        self.writer.write_point3d(14, dim.second_point)?;
        if dim.ext_line_rotation.abs() > 1e-12 {
            self.writer
                .write_double(52, dim.ext_line_rotation.to_degrees())?;
        }
        Ok(())
    }

    fn write_dimension_linear(&mut self, dim: &DimensionLinear, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 0, owner)?; // Linear = 0
        self.writer.write_subclass("AcDbAlignedDimension")?;
        self.writer.write_point3d(13, dim.first_point)?;
        self.writer.write_point3d(14, dim.second_point)?;
        // DXF dimension-line rotation is in degrees.
        self.writer.write_double(50, dim.rotation.to_degrees())?;
        if dim.ext_line_rotation.abs() > 1e-12 {
            self.writer
                .write_double(52, dim.ext_line_rotation.to_degrees())?;
        }
        self.writer.write_subclass("AcDbRotatedDimension")?;
        Ok(())
    }

    fn write_dimension_radius(&mut self, dim: &DimensionRadius, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 4, owner)?; // Radius = 4
        self.writer.write_subclass("AcDbRadialDimension")?;
        self.writer.write_point3d(15, dim.angle_vertex)?;
        self.writer.write_double(40, dim.leader_length)?;
        Ok(())
    }

    fn write_dimension_diameter(&mut self, dim: &DimensionDiameter, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 3, owner)?; // Diameter = 3
        self.writer.write_subclass("AcDbDiametricDimension")?;
        self.writer.write_point3d(15, dim.angle_vertex)?;
        self.writer.write_double(40, dim.leader_length)?;
        Ok(())
    }

    fn write_dimension_angular_2line(&mut self, dim: &DimensionAngular2Ln, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 2, owner)?; // Angular = 2
        self.writer.write_subclass("AcDb2LineAngularDimension")?;
        self.writer.write_point3d(13, dim.first_point)?;
        self.writer.write_point3d(14, dim.second_point)?;
        self.writer.write_point3d(15, dim.angle_vertex)?;
        self.writer.write_point3d(16, dim.definition_point)?;
        Ok(())
    }

    fn write_dimension_angular_3point(&mut self, dim: &DimensionAngular3Pt, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        self.write_dimension_base(&dim.base, 5, owner)?; // 3-point angular = 5
        self.writer.write_subclass("AcDb3PointAngularDimension")?;
        self.writer.write_point3d(13, dim.first_point)?;
        self.writer.write_point3d(14, dim.second_point)?;
        self.writer.write_point3d(15, dim.angle_vertex)?;
        Ok(())
    }

    fn write_dimension_ordinate(&mut self, dim: &DimensionOrdinate, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("DIMENSION")?;
        // Bit 0x40 marks the X datum; clear = Y. (0x80 is reserved for the
        // text-user-positioned flag and must not be reused here.)
        let type_flags = if dim.is_ordinate_type_x { 0x40 } else { 0 };
        self.write_dimension_base(&dim.base, 6 | type_flags, owner)?;
        self.writer.write_subclass("AcDbOrdinateDimension")?;
        self.writer.write_point3d(13, dim.feature_location)?;
        self.writer.write_point3d(14, dim.leader_endpoint)?;
        Ok(())
    }

    /// Write HATCH entity
    fn write_hatch(&mut self, hatch: &Hatch, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("HATCH")?;
        self.write_common_entity_data(&hatch.common, owner)?;
        self.writer.write_subclass("AcDbHatch")?;

        // Elevation point
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, hatch.elevation)?;

        // Normal vector
        self.writer.write_double(210, hatch.normal.x)?;
        self.writer.write_double(220, hatch.normal.y)?;
        self.writer.write_double(230, hatch.normal.z)?;

        // Pattern name
        self.writer.write_string(2, &hatch.pattern.name)?;

        // Solid fill flag
        self.writer.write_i16(70, if hatch.is_solid { 1 } else { 0 })?;

        // Associative flag — clear if boundary handles are all missing / invalid
        let effective_associative = hatch.is_associative && hatch.paths.iter().any(|p| {
            if self.valid_handles.is_empty() {
                !p.boundary_handles.is_empty()
            } else {
                p.boundary_handles.iter().any(|h| *h != Handle::NULL && self.valid_handles.contains(h))
            }
        });
        self.writer
            .write_i16(71, if effective_associative { 1 } else { 0 })?;

        // Number of boundary paths
        self.writer.write_i32(91, hatch.paths.len() as i32)?;

        // Write boundary paths
        for path in &hatch.paths {
            self.write_hatch_boundary_path(path)?;
        }

        // Pattern style
        self.writer.write_i16(75, hatch.style as i16)?;
        self.writer.write_i16(76, hatch.pattern_type as i16)?;

        if !hatch.is_solid {
            self.writer
                .write_double(52, hatch.pattern_angle.to_degrees())?;
            self.writer.write_double(41, hatch.pattern_scale)?;
            self.writer.write_i16(77, if hatch.is_double { 1 } else { 0 })?;

            // Pattern definition lines
            self.writer
                .write_i16(78, hatch.pattern.lines.len() as i16)?;
            for line in &hatch.pattern.lines {
                self.writer.write_double(53, line.angle.to_degrees())?;
                self.writer.write_double(43, line.base_point.x)?;
                self.writer.write_double(44, line.base_point.y)?;
                self.writer.write_double(45, line.offset.x)?;
                self.writer.write_double(46, line.offset.y)?;
                self.writer.write_i16(79, line.dash_lengths.len() as i16)?;
                for dash in &line.dash_lengths {
                    self.writer.write_double(49, *dash)?;
                }
            }
        }

        // Seed points
        self.writer.write_i32(98, hatch.seed_points.len() as i32)?;
        for seed in &hatch.seed_points {
            self.writer.write_double(10, seed.x)?;
            self.writer.write_double(20, seed.y)?;
        }

        Ok(())
    }

    fn write_hatch_boundary_path(&mut self, path: &BoundaryPath) -> Result<()> {
        self.writer.write_i32(92, get_boundary_path_bits(&path.flags) as i32)?;

        if !path.flags.is_polyline() {
            self.writer.write_i32(93, path.edges.len() as i32)?;
        }

        for edge in &path.edges {
            self.write_hatch_edge(edge)?;
        }

        // Associated entities (boundary handles)
        // Filter to only valid handles if valid_handles is populated
        let valid_boundary_handles: Vec<Handle> = if self.valid_handles.is_empty() {
            path.boundary_handles.clone()
        } else {
            path.boundary_handles.iter().copied()
                .filter(|h| *h != Handle::NULL && self.valid_handles.contains(h))
                .collect()
        };
        self.writer.write_i32(97, valid_boundary_handles.len() as i32)?;
        for h in &valid_boundary_handles {
            self.writer.write_handle(330, *h)?;
        }

        Ok(())
    }

    fn write_hatch_edge(&mut self, edge: &BoundaryEdge) -> Result<()> {
        match edge {
            BoundaryEdge::Line(line_edge) => {
                self.writer.write_i16(72, 1)?; // Line type
                self.writer.write_double(10, line_edge.start.x)?;
                self.writer.write_double(20, line_edge.start.y)?;
                self.writer.write_double(11, line_edge.end.x)?;
                self.writer.write_double(21, line_edge.end.y)?;
            }
            BoundaryEdge::CircularArc(arc) => {
                self.writer.write_i16(72, 2)?; // Arc type
                self.writer.write_double(10, arc.center.x)?;
                self.writer.write_double(20, arc.center.y)?;
                self.writer.write_double(40, arc.radius)?;
                self.writer
                    .write_double(50, arc.start_angle.to_degrees())?;
                self.writer.write_double(51, arc.end_angle.to_degrees())?;
                self.writer
                    .write_i16(73, if arc.counter_clockwise { 1 } else { 0 })?;
            }
            BoundaryEdge::EllipticArc(ellipse) => {
                self.writer.write_i16(72, 3)?; // Ellipse type
                self.writer.write_double(10, ellipse.center.x)?;
                self.writer.write_double(20, ellipse.center.y)?;
                self.writer.write_double(11, ellipse.major_axis_endpoint.x)?;
                self.writer.write_double(21, ellipse.major_axis_endpoint.y)?;
                self.writer.write_double(40, ellipse.minor_axis_ratio)?;
                self.writer.write_double(50, ellipse.start_angle)?;
                self.writer.write_double(51, ellipse.end_angle)?;
                self.writer
                    .write_i16(73, if ellipse.counter_clockwise { 1 } else { 0 })?;
            }
            BoundaryEdge::Spline(spline) => {
                self.writer.write_i16(72, 4)?; // Spline type
                self.writer
                    .write_i16(73, if spline.rational { 1 } else { 0 })?;
                self.writer
                    .write_i16(74, if spline.periodic { 1 } else { 0 })?;
                self.writer.write_i32(94, spline.degree)?;
                self.writer.write_i32(95, spline.knots.len() as i32)?;
                self.writer
                    .write_i32(96, spline.control_points.len() as i32)?;
                for knot in &spline.knots {
                    self.writer.write_double(40, *knot)?;
                }
                for point in &spline.control_points {
                    self.writer.write_double(10, point.x)?;
                    self.writer.write_double(20, point.y)?;
                    if spline.rational {
                        self.writer.write_double(42, point.z)?; // z stores weight
                    }
                }
                // Fit data (R2010+)
                if self.dxf_version >= DxfVersion::AC1024 {
                    self.writer.write_i32(97, spline.fit_points.len() as i32)?;
                    for fp in &spline.fit_points {
                        self.writer.write_double(11, fp.x)?;
                        self.writer.write_double(21, fp.y)?;
                    }
                    if !spline.fit_points.is_empty() {
                        self.writer.write_double(12, spline.start_tangent.x)?;
                        self.writer.write_double(22, spline.start_tangent.y)?;
                        self.writer.write_double(13, spline.end_tangent.x)?;
                        self.writer.write_double(23, spline.end_tangent.y)?;
                    }
                }
            }
            BoundaryEdge::Polyline(poly) => {
                let has_bulge = poly.has_bulge();
                self.writer.write_i16(72, if has_bulge { 1 } else { 0 })?;
                self.writer
                    .write_i16(73, if poly.is_closed { 1 } else { 0 })?;
                self.writer.write_i32(93, poly.vertices.len() as i32)?;
                for vertex in &poly.vertices {
                    self.writer.write_double(10, vertex.x)?;
                    self.writer.write_double(20, vertex.y)?;
                    if has_bulge {
                        self.writer.write_double(42, vertex.z)?; // z stores bulge
                    }
                }
            }
        }
        Ok(())
    }

    /// Write SOLID entity
    fn write_solid(&mut self, solid: &Solid, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("SOLID")?;
        self.write_common_entity_data(&solid.common, owner)?;
        self.writer.write_subclass("AcDbTrace")?;
        self.writer.write_point3d(10, solid.first_corner)?;
        self.writer.write_point3d(11, solid.second_corner)?;
        self.writer.write_point3d(12, solid.third_corner)?;
        self.writer.write_point3d(13, solid.fourth_corner)?;
        if solid.thickness != 0.0 {
            self.writer.write_double(39, solid.thickness)?;
        }
        self.write_normal(solid.normal)?;
        Ok(())
    }

    /// Write 3DFACE entity
    fn write_face3d(&mut self, face: &Face3D, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("3DFACE")?;
        self.write_common_entity_data(&face.common, owner)?;
        self.writer.write_subclass("AcDbFace")?;
        self.writer.write_point3d(10, face.first_corner)?;
        self.writer.write_point3d(11, face.second_corner)?;
        self.writer.write_point3d(12, face.third_corner)?;
        self.writer.write_point3d(13, face.fourth_corner)?;
        if face.invisible_edges != InvisibleEdgeFlags::NONE {
            let edge_bits = get_invisible_edge_bits(&face.invisible_edges);
            self.writer.write_i16(70, edge_bits as i16)?;
        }
        Ok(())
    }

    /// Write INSERT entity
    fn write_insert(&mut self, insert: &Insert, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("INSERT")?;
        self.write_common_entity_data(&insert.common, owner)?;
        self.writer.write_subclass(insert.subclass_marker())?;
        // Has-attributes flag (group code 66)
        if insert.has_attributes() {
            self.writer.write_i16(66, 1)?;
        }
        self.writer.write_string(2, &insert.block_name)?;
        self.writer.write_point3d(10, insert.insert_point)?;
        if insert.x_scale() != 1.0 {
            self.writer.write_double(41, insert.x_scale())?;
        }
        if insert.y_scale() != 1.0 {
            self.writer.write_double(42, insert.y_scale())?;
        }
        if insert.z_scale() != 1.0 {
            self.writer.write_double(43, insert.z_scale())?;
        }
        if insert.rotation != 0.0 {
            self.writer.write_double(50, insert.rotation.to_degrees())?;
        }
        if insert.column_count > 1 {
            self.writer.write_i16(70, insert.column_count as i16)?;
        }
        if insert.row_count > 1 {
            self.writer.write_i16(71, insert.row_count as i16)?;
        }
        if insert.column_spacing != 0.0 {
            self.writer.write_double(44, insert.column_spacing)?;
        }
        if insert.row_spacing != 0.0 {
            self.writer.write_double(45, insert.row_spacing)?;
        }
        self.write_normal(insert.normal)?;

        // Write child ATTRIB entities + SEQEND when attributes are present
        if insert.has_attributes() {
            let insert_handle = insert.handle();
            for att in &insert.attributes {
                self.write_attrib(att, insert_handle)?;
            }
            // SEQEND terminates the attribute sequence
            let seqend_handle = self.allocate_handle();
            self.writer.write_entity_type("SEQEND")?;
            self.writer.write_handle(5, seqend_handle)?;
            self.writer.write_handle(330, insert_handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &insert.common.layer)?;
        }
        Ok(())
    }

    /// Write BLOCK entity
    fn write_block_entity(&mut self, block: &Block, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("BLOCK")?;
        self.write_common_entity_data(&block.common, owner)?;
        self.writer.write_subclass("AcDbBlockBegin")?;
        self.writer.write_string(2, &block.name)?;
        self.writer.write_i16(70, 0)?; // Block flags
        self.writer.write_point3d(10, block.base_point)?;
        self.writer.write_string(3, &block.name)?;
        if !block.xref_path.is_empty() {
            self.writer.write_string(1, &block.xref_path)?;
        }
        if !block.description.is_empty() {
            self.writer.write_string(4, &block.description)?;
        }
        Ok(())
    }

    /// Write ENDBLK entity
    fn write_block_end(&mut self, block_end: &BlockEnd, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ENDBLK")?;
        self.write_common_entity_data(&block_end.common, owner)?;
        self.writer.write_subclass("AcDbBlockEnd")?;
        Ok(())
    }

    /// Write RAY entity
    fn write_ray(&mut self, ray: &Ray, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("RAY")?;
        self.write_common_entity_data(&ray.common, owner)?;
        self.writer.write_subclass("AcDbRay")?;
        self.writer.write_point3d(10, ray.base_point)?;
        self.writer.write_point3d(11, ray.direction)?;
        Ok(())
    }

    /// Write XLINE entity
    fn write_xline(&mut self, xline: &XLine, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("XLINE")?;
        self.write_common_entity_data(&xline.common, owner)?;
        self.writer.write_subclass("AcDbXline")?;
        self.writer.write_point3d(10, xline.base_point)?;
        self.writer.write_point3d(11, xline.direction)?;
        Ok(())
    }

    /// Write POLYLINE (3D) entity
    fn write_polyline3d(&mut self, polyline: &Polyline3D, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("POLYLINE")?;
        self.write_common_entity_data(&polyline.common, owner)?;
        self.writer.write_subclass("AcDb3dPolyline")?;

        // Entities follow flag (VERTEX records follow)
        self.writer.write_i16(66, 1)?;
        
        // Dummy point with elevation (ACadSharp pattern)
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, polyline.elevation)?;
        
        // Polyline flags (bit 8 = 3D polyline)
        self.writer.write_i16(70, polyline.flags.to_bits() as i16)?;
        
        // Write vertices with proper subclass markers
        let polyline_handle = polyline.handle();
        for vertex in polyline.vertices.iter() {
            let vertex_handle = if vertex.handle.is_null() {
                self.allocate_handle()
            } else {
                vertex.handle
            };
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, vertex_handle)?;
            self.writer.write_handle(330, polyline_handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &vertex.layer)?;
            // Propagate parent color to vertex so CAD doesn't flag mismatch
            if polyline.common.color != Color::ByLayer {
                self.writer.write_color(62, polyline.common.color)?;
            }
            self.writer.write_subclass("AcDbVertex")?;
            self.writer.write_subclass("AcDb3dPolylineVertex")?;
            self.writer.write_point3d(10, vertex.position)?;
            self.writer.write_i16(70, vertex.flags as i16)?;
        }
        
        // SEQEND
        self.writer.write_entity_type("SEQEND")?;
        let seqend_handle = self.allocate_handle();
        self.writer.write_handle(5, seqend_handle)?;
        self.writer.write_handle(330, polyline_handle)?;
        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &polyline.common.layer)?;
        
        Ok(())
    }

    /// Write VIEWPORT entity
    fn write_viewport(&mut self, viewport: &Viewport, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("VIEWPORT")?;
        self.write_common_entity_data(&viewport.common, owner)?;
        self.writer.write_subclass("AcDbViewport")?;
        
        // Center point
        self.writer.write_point3d(10, viewport.center)?;
        
        // Width and height
        self.writer.write_double(40, viewport.width)?;
        self.writer.write_double(41, viewport.height)?;
        
        // Viewport ID
        self.writer.write_i16(68, viewport.id)?;
        
        // Status
        self.writer.write_i32(90, viewport.status.to_bits())?;
        
        // View center
        self.writer.write_double(12, viewport.view_center.x)?;
        self.writer.write_double(22, viewport.view_center.y)?;
        
        // Snap base
        self.writer.write_double(13, viewport.snap_base.x)?;
        self.writer.write_double(23, viewport.snap_base.y)?;
        
        // Snap spacing
        self.writer.write_double(14, viewport.snap_spacing.x)?;
        self.writer.write_double(24, viewport.snap_spacing.y)?;
        
        // Grid spacing
        self.writer.write_double(15, viewport.grid_spacing.x)?;
        self.writer.write_double(25, viewport.grid_spacing.y)?;
        
        // View direction
        self.writer.write_double(16, viewport.view_direction.x)?;
        self.writer.write_double(26, viewport.view_direction.y)?;
        self.writer.write_double(36, viewport.view_direction.z)?;
        
        // View target
        self.writer.write_double(17, viewport.view_target.x)?;
        self.writer.write_double(27, viewport.view_target.y)?;
        self.writer.write_double(37, viewport.view_target.z)?;
        
        // Lens length
        self.writer.write_double(42, viewport.lens_length)?;
        
        // Front and back clipping
        self.writer.write_double(43, viewport.front_clip_z)?;
        self.writer.write_double(44, viewport.back_clip_z)?;
        
        // View height
        self.writer.write_double(45, viewport.view_height)?;
        
        // Snap and twist angles
        self.writer.write_double(50, viewport.snap_angle)?;
        self.writer.write_double(51, viewport.twist_angle)?;
        
        // Circle sides
        self.writer.write_i16(72, viewport.circle_sides)?;
        
        // Frozen layers (code 331)
        for frozen_layer in &viewport.frozen_layers {
            if !frozen_layer.is_null() {
                self.writer.write_handle(331, *frozen_layer)?;
            }
        }
        
        // Render mode
        self.writer.write_byte(281, viewport.render_mode.to_value() as u8)?;
        
        // UCS per viewport
        if viewport.ucs_per_viewport {
            self.writer.write_i16(71, 1)?;
        }
        
        // UCS origin, axes
        if viewport.ucs_origin != Vector3::ZERO {
            self.writer.write_double(110, viewport.ucs_origin.x)?;
            self.writer.write_double(120, viewport.ucs_origin.y)?;
            self.writer.write_double(130, viewport.ucs_origin.z)?;
        }
        if viewport.ucs_x_axis != Vector3::ZERO {
            self.writer.write_double(111, viewport.ucs_x_axis.x)?;
            self.writer.write_double(121, viewport.ucs_x_axis.y)?;
            self.writer.write_double(131, viewport.ucs_x_axis.z)?;
        }
        if viewport.ucs_y_axis != Vector3::ZERO {
            self.writer.write_double(112, viewport.ucs_y_axis.x)?;
            self.writer.write_double(122, viewport.ucs_y_axis.y)?;
            self.writer.write_double(132, viewport.ucs_y_axis.z)?;
        }
        
        // Elevation
        if viewport.elevation != 0.0 {
            self.writer.write_double(146, viewport.elevation)?;
        }
        
        // Grid major
        if viewport.grid_major != 0 {
            self.writer.write_i16(61, viewport.grid_major)?;
        }
        
        Ok(())
    }

    /// Write ATTDEF entity
    fn write_attdef(&mut self, attdef: &AttributeDefinition, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ATTDEF")?;
        self.write_common_entity_data(&attdef.common, owner)?;
        self.writer.write_subclass("AcDbText")?;
        
        // Insertion point
        self.writer.write_point3d(10, attdef.insertion_point)?;
        
        // Text height
        self.writer.write_double(40, attdef.height)?;
        
        // Default value
        self.writer.write_string(1, &attdef.default_value)?;
        
        // Rotation
        self.writer.write_double(50, attdef.rotation.to_degrees())?;
        
        // Width factor
        self.writer.write_double(41, attdef.width_factor)?;
        
        // Oblique angle
        self.writer.write_double(51, attdef.oblique_angle.to_degrees())?;
        
        // Text style
        self.writer.write_string(7, &attdef.text_style)?;
        
        // Text generation flags
        self.writer.write_i16(71, attdef.text_generation_flags)?;
        
        // Horizontal alignment
        self.writer.write_i16(72, attdef.horizontal_alignment.to_value())?;
        
        // Alignment point (base code 11 → writes 11, 21, 31)
        self.writer.write_point3d(11, attdef.alignment_point)?;
        
        // Normal
        self.writer.write_point3d(210, attdef.normal)?;
        
        self.writer.write_subclass("AcDbAttributeDefinition")?;
        
        // Tag
        self.writer.write_string(2, &attdef.tag)?;
        
        // Attribute flags
        self.writer.write_i16(70, attdef.flags.to_bits() as i16)?;
        
        // Field length
        self.writer.write_i16(73, attdef.field_length)?;
        
        // Vertical alignment
        self.writer.write_i16(74, attdef.vertical_alignment.to_value())?;
        
        // Prompt
        self.writer.write_string(3, &attdef.prompt)?;
        
        Ok(())
    }

    /// Write ATTRIB entity
    fn write_attrib(&mut self, attrib: &AttributeEntity, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ATTRIB")?;
        self.write_common_entity_data(&attrib.common, owner)?;
        self.writer.write_subclass("AcDbText")?;
        
        // Insertion point
        self.writer.write_point3d(10, attrib.insertion_point)?;
        
        // Text height
        self.writer.write_double(40, attrib.height)?;
        
        // Value
        self.writer.write_string(1, &attrib.value)?;
        
        // Rotation
        self.writer.write_double(50, attrib.rotation.to_degrees())?;
        
        // Width factor
        self.writer.write_double(41, attrib.width_factor)?;
        
        // Oblique angle
        self.writer.write_double(51, attrib.oblique_angle.to_degrees())?;
        
        // Text style
        self.writer.write_string(7, &attrib.text_style)?;
        
        // Text generation flags
        self.writer.write_i16(71, attrib.text_generation_flags)?;
        
        // Horizontal alignment
        self.writer.write_i16(72, attrib.horizontal_alignment.to_value())?;
        
        // Alignment point (base code 11 → writes 11, 21, 31)
        self.writer.write_point3d(11, attrib.alignment_point)?;
        
        // Normal
        self.writer.write_point3d(210, attrib.normal)?;
        
        self.writer.write_subclass("AcDbAttribute")?;
        
        // Tag
        self.writer.write_string(2, &attrib.tag)?;
        
        // Attribute flags
        self.writer.write_i16(70, attrib.flags.to_bits() as i16)?;
        
        // Field length
        self.writer.write_i16(73, attrib.field_length)?;
        
        // Vertical alignment
        self.writer.write_i16(74, attrib.vertical_alignment.to_value())?;
        
        Ok(())
    }

    /// Write LEADER entity
    fn write_leader(&mut self, leader: &Leader, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("LEADER")?;
        self.write_common_entity_data(&leader.common, owner)?;
        self.writer.write_subclass("AcDbLeader")?;
        
        // Dimension style
        self.writer.write_string(3, &leader.dimension_style)?;
        
        // Arrow head flag
        self.writer.write_i16(71, if leader.arrow_enabled { 1 } else { 0 })?;
        
        // Path type
        self.writer.write_i16(72, leader.path_type.to_value())?;
        
        // Creation type
        self.writer.write_i16(73, leader.creation_type.to_value())?;
        
        // Hookline direction
        self.writer.write_i16(74, leader.hookline_direction.to_value())?;
        
        // Hookline flag
        self.writer.write_i16(75, if leader.hookline_enabled { 1 } else { 0 })?;
        
        // Text height
        self.writer.write_double(40, leader.text_height)?;
        
        // Text width
        self.writer.write_double(41, leader.text_width)?;
        
        // Number of vertices
        self.writer.write_i16(76, leader.vertices.len() as i16)?;
        
        // Vertices
        for vertex in &leader.vertices {
            self.writer.write_point3d(10, *vertex)?;
        }
        
        // Normal
        self.writer.write_point3d(210, leader.normal)?;
        
        // Horizontal direction
        self.writer.write_point3d(211, leader.horizontal_direction)?;
        
        // Block offset
        self.writer.write_point3d(212, leader.block_offset)?;
        
        // Annotation offset
        self.writer.write_point3d(213, leader.annotation_offset)?;
        
        Ok(())
    }

    /// Write the OBJECTS section
    pub fn write_objects(&mut self, document: &CadDocument) -> Result<()> {
        self.writer.write_section_start("OBJECTS")?;

        // DXF spec requires the root named object dictionary to be the
        // very first object in the OBJECTS section.
        // The header handle may be NULL/invalid (e.g. after DWG reading where
        // handle references aren't fully resolved), so fall back to scanning
        // objects for the root dictionary (owner == NULL).
        let mut root_handle = document.header.named_objects_dict_handle;
        if root_handle.is_null()
            || !matches!(document.objects.get(&root_handle), Some(ObjectType::Dictionary(_)))
        {
            root_handle = Self::find_root_dict_handle(&document.objects);
        }
        if let Some(ObjectType::Dictionary(root_dict)) = document.objects.get(&root_handle) {
            self.write_dictionary(root_dict, &document.objects)?;
        }

        // Write remaining objects (skip the root dictionary already written)
        for (handle, object) in document.objects.iter() {
            if *handle == root_handle {
                continue;
            }
            let object = object;
            match object {
                ObjectType::Dictionary(dict) => self.write_dictionary(dict, &document.objects)?,
                ObjectType::Layout(layout) => self.write_layout(layout)?,
                ObjectType::XRecord(xrecord) => self.write_xrecord(xrecord)?,
                ObjectType::Group(group) => self.write_group(group)?,
                ObjectType::MLineStyle(mlinestyle) => self.write_mlinestyle(mlinestyle)?,
                ObjectType::ImageDefinition(imagedef) => self.write_image_definition(imagedef)?,
                ObjectType::UnderlayDefinition(def) => self.write_underlay_definition(def)?,
                ObjectType::PlotSettings(plotsettings) => self.write_plot_settings(plotsettings)?,
                ObjectType::MultiLeaderStyle(style) => self.write_multileader_style(style)?,
                ObjectType::TableStyle(style) => self.write_table_style(style)?,
                ObjectType::Scale(scale) => self.write_scale(scale)?,
                ObjectType::SortEntitiesTable(table) => self.write_sort_entities_table(table)?,
                ObjectType::DictionaryVariable(var) => self.write_dictionary_variable(var)?,
                ObjectType::VisualStyle(obj) => self.write_visualstyle(obj)?,
                ObjectType::Material(obj) => self.write_material(obj)?,
                ObjectType::ImageDefinitionReactor(obj) => self.write_imagedef_reactor(obj)?,
                ObjectType::GeoData(obj) => self.write_stub_handle_only("GEODATA", obj.handle, obj.owner)?,
                ObjectType::SpatialFilter(obj) => self.write_spatial_filter(obj)?,
                ObjectType::RasterVariables(obj) => self.write_raster_variables(obj)?,
                ObjectType::BookColor(obj) => self.write_bookcolor(obj)?,
                ObjectType::PlaceHolder(obj) => self.write_stub_handle_only("ACDBPLACEHOLDER", obj.handle, obj.owner)?,
                ObjectType::DictionaryWithDefault(obj) => self.write_dict_with_default(obj, &document.objects)?,
                ObjectType::WipeoutVariables(obj) => self.write_wipeout_variables(obj)?,
                ObjectType::Unknown { type_name, handle, owner, raw_dxf_codes, .. } => {
                    self.write_unknown_object(type_name, *handle, *owner, raw_dxf_codes.as_deref())?;
                }
            }
        }

        self.writer.write_section_end()?;
        Ok(())
    }

    /// Find the root named-objects dictionary by scanning for a Dictionary
    /// with owner == NULL.  Prefers the one with the most entries.
    fn find_root_dict_handle(objects: &std::collections::HashMap<Handle, ObjectType>) -> Handle {
        let mut best = Handle::NULL;
        let mut best_count = 0usize;
        for (handle, obj) in objects {
            if let ObjectType::Dictionary(dict) = obj {
                if dict.owner.is_null() {
                    if dict.entries.len() > best_count
                        || (dict.entries.len() == best_count && handle.value() > best.value())
                    {
                        best = *handle;
                        best_count = dict.entries.len();
                    }
                }
            }
        }
        best
    }

    fn write_dictionary(&mut self, dict: &Dictionary, objects: &std::collections::HashMap<Handle, ObjectType>) -> Result<()> {
        self.writer.write_string(0, "DICTIONARY")?;
        self.writer.write_handle(5, dict.handle)?;
        let dict_owner = if dict.owner == Handle::NULL
            || self.valid_handles.is_empty()
            || self.valid_handles.contains(&dict.owner)
            || self.root_dict_handle == Handle::NULL
        {
            dict.owner
        } else {
            self.root_dict_handle
        };
        self.writer.write_handle(330, dict_owner)?;
        self.writer.write_subclass("AcDbDictionary")?;
        self.writer
            .write_byte(280, if dict.hard_owner { 1 } else { 0 })?;
        self.writer.write_byte(281, dict.duplicate_cloning as u8)?;

        for (key, handle) in &dict.entries {
            // Skip entries pointing to objects that don't exist in the document,
            // OR that exist only as an Unknown DWG object with no DXF form (and
            // are therefore filtered out of valid_handles and never written).
            // Writing dangling references causes CAD programs to report audit
            // errors.
            if !objects.contains_key(handle)
                || (!self.valid_handles.is_empty() && !self.valid_handles.contains(handle))
            {
                continue;
            }
            self.writer.write_string(3, key)?;
            self.writer.write_handle(350, *handle)?;
        }

        Ok(())
    }

    fn write_layout(&mut self, layout: &Layout) -> Result<()> {
        self.writer.write_string(0, "LAYOUT")?;
        self.writer.write_handle(5, layout.handle)?;

        // Extension dictionary
        if let Some(xdict) = layout.xdictionary_handle {
            if xdict != Handle::NULL && (self.valid_handles.is_empty() || self.valid_handles.contains(&xdict)) {
                self.writer.write_string(102, "{ACAD_XDICTIONARY")?;
                self.writer.write_handle(360, xdict)?;
                self.writer.write_string(102, "}")?;
            }
        }

        // Reactors (filter out references to non-existent objects)
        if !layout.reactors.is_empty() {
            let valid_reactors: Vec<Handle> = if self.valid_handles.is_empty() {
                layout.reactors.clone()
            } else {
                layout.reactors.iter().copied()
                    .filter(|r| self.valid_handles.contains(r))
                    .collect()
            };
            if !valid_reactors.is_empty() {
                self.writer.write_string(102, "{ACAD_REACTORS")?;
                for r in &valid_reactors {
                    self.writer.write_handle(330, *r)?;
                }
                self.writer.write_string(102, "}")?;
            }
        }

        self.writer.write_handle(330, layout.owner)?;
        self.writer.write_subclass("AcDbPlotSettings")?;

        // Write plot settings: use preserved raw codes if available,
        // otherwise write minimal defaults.
        if let Some(ref codes) = layout.raw_plot_settings_codes {
            for (code, value) in codes {
                self.writer.write_string(*code, value)?;
            }
        } else {
            self.writer.write_string(1, "")?; // Page setup name
            self.writer.write_string(2, "")?; // Printer/plotter name
            self.writer.write_string(4, "")?; // Paper size
            self.writer.write_string(6, "")?; // Plot view name
            self.writer.write_double(40, 0.0)?; // Left margin
            self.writer.write_double(41, 0.0)?; // Bottom margin
            self.writer.write_double(42, 0.0)?; // Right margin
            self.writer.write_double(43, 0.0)?; // Top margin
            self.writer.write_double(44, 0.0)?; // Paper width
            self.writer.write_double(45, 0.0)?; // Paper height
            self.writer.write_double(46, 0.0)?; // Plot origin X
            self.writer.write_double(47, 0.0)?; // Plot origin Y
            self.writer.write_double(48, 0.0)?; // Plot window X1
            self.writer.write_double(49, 0.0)?; // Plot window Y1
            self.writer.write_double(140, 0.0)?; // Plot window X2
            self.writer.write_double(141, 0.0)?; // Plot window Y2
            self.writer.write_double(142, 1.0)?; // Numerator of custom print scale
            self.writer.write_double(143, 1.0)?; // Denominator of custom print scale
            // Bit 1024 (0x400) = Model type flag — required for Model layouts
            let plot_flags: i16 = if layout.name == "Model" { 1024 } else { 0 };
            self.writer.write_i16(70, plot_flags)?;
            self.writer.write_i16(72, 0)?; // Plot paper units
            self.writer.write_i16(73, 0)?; // Plot rotation
            self.writer.write_i16(74, 0)?; // Plot type
        }

        self.writer.write_subclass("AcDbLayout")?;
        self.writer.write_string(1, &layout.name)?;
        self.writer.write_i16(70, layout.flags)?;
        self.writer.write_i16(71, layout.tab_order)?;
        self.writer.write_double(10, layout.min_limits.0)?;
        self.writer.write_double(20, layout.min_limits.1)?;
        self.writer.write_double(11, layout.max_limits.0)?;
        self.writer.write_double(21, layout.max_limits.1)?;
        self.writer.write_double(12, layout.insertion_base.0)?;
        self.writer.write_double(22, layout.insertion_base.1)?;
        self.writer.write_double(32, layout.insertion_base.2)?;
        self.writer.write_double(14, layout.min_extents.0)?;
        self.writer.write_double(24, layout.min_extents.1)?;
        self.writer.write_double(34, layout.min_extents.2)?;
        self.writer.write_double(15, layout.max_extents.0)?;
        self.writer.write_double(25, layout.max_extents.1)?;
        self.writer.write_double(35, layout.max_extents.2)?;
        self.writer.write_double(146, layout.elevation)?;
        self.writer.write_double(13, layout.ucs_origin.0)?;
        self.writer.write_double(23, layout.ucs_origin.1)?;
        self.writer.write_double(33, layout.ucs_origin.2)?;
        self.writer.write_double(16, layout.ucs_x_axis.0)?;
        self.writer.write_double(26, layout.ucs_x_axis.1)?;
        self.writer.write_double(36, layout.ucs_x_axis.2)?;
        self.writer.write_double(17, layout.ucs_y_axis.0)?;
        self.writer.write_double(27, layout.ucs_y_axis.1)?;
        self.writer.write_double(37, layout.ucs_y_axis.2)?;
        self.writer.write_i16(76, layout.ucs_ortho_type)?;
        self.writer.write_handle(330, layout.block_record)?;
        if layout.viewport != Handle::NULL {
            self.writer.write_handle(331, layout.viewport)?;
        }

        Ok(())
    }

    fn write_xrecord(&mut self, xrecord: &XRecord) -> Result<()> {
        use crate::objects::XRecordValue;

        self.writer.write_string(0, "XRECORD")?;
        self.writer.write_handle(5, xrecord.handle)?;
        self.writer.write_handle(330, xrecord.owner)?;
        self.writer.write_subclass("AcDbXrecord")?;
        self.writer.write_byte(280, xrecord.cloning_flags.to_code() as u8)?;

        // Write each entry's group code and value
        for entry in xrecord.iter() {
            match &entry.value {
                XRecordValue::String(s) => {
                    self.writer.write_string(entry.code, s)?;
                }
                XRecordValue::Double(d) => {
                    self.writer.write_double(entry.code, *d)?;
                }
                XRecordValue::Int16(i) => {
                    self.writer.write_i16(entry.code, *i)?;
                }
                XRecordValue::Int32(i) => {
                    self.writer.write_i32(entry.code, *i)?;
                }
                XRecordValue::Int64(i) => {
                    // Write as i32, truncating if needed (DXF doesn't have native i64 codes for all ranges)
                    self.writer.write_i32(entry.code, *i as i32)?;
                }
                XRecordValue::Byte(b) => {
                    self.writer.write_i16(entry.code, *b as i16)?;
                }
                XRecordValue::Bool(b) => {
                    self.writer.write_i16(entry.code, if *b { 1 } else { 0 })?;
                }
                XRecordValue::Handle(h) => {
                    self.writer.write_handle(entry.code, *h)?;
                }
                XRecordValue::Point3D(x, y, z) => {
                    self.writer.write_double(entry.code, *x)?;
                    self.writer.write_double(entry.code + 10, *y)?;
                    self.writer.write_double(entry.code + 20, *z)?;
                }
                XRecordValue::Chunk(data) => {
                    self.writer.write_binary(entry.code, data)?;
                }
            }
        }

        Ok(())
    }

    fn write_group(&mut self, group: &Group) -> Result<()> {
        self.writer.write_string(0, "GROUP")?;
        self.writer.write_handle(5, group.handle)?;
        self.writer.write_handle(330, group.owner)?;
        self.writer.write_subclass("AcDbGroup")?;

        // Group description (code 300)
        self.writer.write_string(300, &group.description)?;

        // Unnamed flag (code 70) - 1 if unnamed, 0 if named
        self.writer
            .write_i16(70, if group.is_unnamed() { 1 } else { 0 })?;

        // Selectable flag (code 71)
        self.writer
            .write_i16(71, if group.selectable { 1 } else { 0 })?;

        // Entity handles (code 340)
        for entity_handle in group.iter() {
            self.writer.write_handle(340, *entity_handle)?;
        }

        Ok(())
    }

    fn write_mlinestyle(&mut self, style: &MLineStyle) -> Result<()> {
        self.writer.write_string(0, "MLINESTYLE")?;
        self.writer.write_handle(5, style.handle)?;
        self.writer.write_handle(330, style.owner)?;
        self.writer.write_subclass("AcDbMlineStyle")?;

        // Style name (code 2)
        self.writer.write_string(2, &style.name)?;

        // Flags (code 70)
        self.writer.write_i16(70, style.flags.to_bits() as i16)?;

        // Description (code 3)
        self.writer.write_string(3, &style.description)?;

        // Fill color (code 62)
        let fill_color_index = match style.fill_color {
            Color::ByLayer => 256,
            Color::ByBlock => 0,
            Color::Index(i) => i as i16,
            Color::Rgb { .. } => 256, // Fall back to ByLayer for RGB color
        };
        self.writer.write_i16(62, fill_color_index)?;

        // Start angle (code 51) — DXF expects degrees
        self.writer.write_double(51, style.start_angle.to_degrees())?;

        // End angle (code 52) — DXF expects degrees
        self.writer.write_double(52, style.end_angle.to_degrees())?;

        // Number of elements (code 71)
        self.writer.write_i16(71, style.element_count() as i16)?;

        // Write each element
        for element in style.iter() {
            // Element offset (code 49)
            self.writer.write_double(49, element.offset)?;

            // Element color (code 62)
            let elem_color_index = match element.color {
                Color::ByLayer => 256,
                Color::ByBlock => 0,
                Color::Index(i) => i as i16,
                Color::Rgb { .. } => 256,
            };
            self.writer.write_i16(62, elem_color_index)?;

            // Element linetype (code 6)
            self.writer.write_string(6, &element.linetype)?;
        }

        Ok(())
    }

    fn write_image_definition(&mut self, imagedef: &ImageDefinition) -> Result<()> {
        self.writer.write_string(0, "IMAGEDEF")?;
        self.writer.write_handle(5, imagedef.handle)?;
        self.writer.write_handle(330, imagedef.owner)?;
        self.writer.write_subclass("AcDbRasterImageDef")?;

        // Class version (code 90)
        self.writer.write_i32(90, imagedef.class_version)?;

        // File name (code 1)
        self.writer.write_string(1, &imagedef.file_name)?;

        // Image size in pixels (codes 10, 20)
        self.writer
            .write_double(10, imagedef.size_in_pixels.0 as f64)?;
        self.writer
            .write_double(20, imagedef.size_in_pixels.1 as f64)?;

        // Default pixel size (codes 11, 21)
        self.writer.write_double(11, imagedef.pixel_size.0)?;
        self.writer.write_double(21, imagedef.pixel_size.1)?;

        // Is loaded (code 280)
        self.writer
            .write_byte(280, if imagedef.is_loaded { 1 } else { 0 })?;

        // Resolution units (code 281)
        self.writer
            .write_byte(281, imagedef.resolution_unit.to_code() as u8)?;

        Ok(())
    }

    /// Write a PDF/DWF/DGN underlay definition object.
    fn write_underlay_definition(
        &mut self,
        def: &crate::objects::UnderlayDefinition,
    ) -> Result<()> {
        self.writer.write_string(0, def.entity_name())?;
        self.writer.write_handle(5, def.handle)?;
        self.writer.write_handle(330, def.owner_handle)?;
        self.writer.write_subclass("AcDbUnderlayDefinition")?;
        self.writer.write_string(1, &def.file_path)?;
        self.writer.write_string(2, &def.page_name)?;
        Ok(())
    }

    fn write_plot_settings(&mut self, settings: &PlotSettings) -> Result<()> {
        self.writer.write_string(0, "PLOTSETTINGS")?;
        self.writer.write_handle(5, settings.handle)?;
        self.writer.write_handle(330, settings.owner)?;
        self.writer.write_subclass("AcDbPlotSettings")?;

        // Page setup name (code 1)
        self.writer.write_string(1, &settings.page_name)?;

        // Printer/plotter name (code 2)
        self.writer.write_string(2, &settings.printer_name)?;

        // Paper size (code 4)
        self.writer.write_string(4, &settings.paper_size)?;

        // Plot view name (code 6)
        self.writer.write_string(6, &settings.plot_view_name)?;

        // Style sheet (code 7)
        self.writer.write_string(7, &settings.current_style_sheet)?;

        // Unprintable margins (codes 40-43)
        self.writer.write_double(40, settings.margins.left)?;
        self.writer.write_double(41, settings.margins.bottom)?;
        self.writer.write_double(42, settings.margins.right)?;
        self.writer.write_double(43, settings.margins.top)?;

        // Paper size (codes 44, 45)
        self.writer.write_double(44, settings.paper_width)?;
        self.writer.write_double(45, settings.paper_height)?;

        // Plot origin (codes 46, 47)
        self.writer.write_double(46, settings.origin_x)?;
        self.writer.write_double(47, settings.origin_y)?;

        // Plot window (codes 48, 49, 140, 141)
        self.writer
            .write_double(48, settings.plot_window.lower_left_x)?;
        self.writer
            .write_double(49, settings.plot_window.lower_left_y)?;
        self.writer
            .write_double(140, settings.plot_window.upper_right_x)?;
        self.writer
            .write_double(141, settings.plot_window.upper_right_y)?;

        // Custom scale (codes 142, 143)
        self.writer.write_double(142, settings.scale_numerator)?;
        self.writer.write_double(143, settings.scale_denominator)?;

        // Flags (code 70)
        self.writer.write_i16(70, settings.flags.to_bits() as i16)?;

        // Paper units (code 72)
        self.writer.write_i16(72, settings.paper_units.to_code())?;

        // Rotation (code 73)
        self.writer.write_i16(73, settings.rotation.to_code())?;

        // Plot type (code 74)
        self.writer.write_i16(74, settings.plot_type.to_code())?;

        // Standard scale type (code 75)
        self.writer.write_i16(75, settings.scale_type.to_code())?;

        // Shade plot mode (code 76)
        self.writer
            .write_i16(76, settings.shade_plot_mode.to_code())?;

        // Shade plot resolution level (code 77)
        self.writer
            .write_i16(77, settings.shade_plot_resolution.to_code())?;

        // Shade plot custom DPI (code 78)
        self.writer.write_i16(78, settings.shade_plot_dpi)?;

        Ok(())
    }

    /// Write MultiLeaderStyle object
    fn write_multileader_style(&mut self, style: &MultiLeaderStyle) -> Result<()> {
        self.writer.write_string(0, "MLEADERSTYLE")?;
        self.writer.write_handle(5, style.handle)?;
        self.writer.write_handle(330, style.owner_handle)?;
        self.writer.write_subclass("AcDbMLeaderStyle")?;

        // Content type
        self.writer.write_i16(170, style.content_type as i16)?;

        // Draw mleader order type
        self.writer.write_i16(171, style.multileader_draw_order as i16)?;

        // Draw leader order type
        self.writer.write_i16(172, style.leader_draw_order as i16)?;

        // Max leader points
        self.writer.write_i32(90, style.max_leader_points)?;

        // First segment angle constraint
        self.writer.write_double(40, style.first_segment_angle)?;

        // Second segment angle constraint
        self.writer.write_double(41, style.second_segment_angle)?;

        // Leader line type
        self.writer.write_i16(173, style.path_type as i16)?;

        // Leader line color
        self.write_color_i32(91, style.line_color)?;

        // Leader line type handle (use ByLayer as fallback)
        {
            let h = style.line_type_handle
                .filter(|h| *h != Handle::NULL)
                .unwrap_or(self.bylayer_linetype_handle);
            if h != Handle::NULL {
                self.writer.write_handle(340, h)?;
            }
        }

        // Leader line weight
        self.writer.write_i32(92, style.line_weight.value() as i32)?;

        // Enable landing
        self.writer.write_bool(290, style.enable_landing)?;

        // Landing gap
        self.writer.write_double(42, style.landing_gap)?;

        // Enable dogleg
        self.writer.write_bool(291, style.enable_dogleg)?;

        // Dogleg length
        self.writer.write_double(43, style.landing_distance)?;

        // Style name
        self.writer.write_string(3, &style.name)?;

        // Arrow head block handle
        if let Some(h) = style.arrowhead_handle {
            self.writer.write_handle(341, h)?;
        }

        // Arrow head size
        self.writer.write_double(44, style.arrowhead_size)?;

        // Default mtext contents
        self.writer.write_string(300, &style.default_text)?;

        // Text style handle
        if let Some(h) = style.text_style_handle {
            self.writer.write_handle(342, h)?;
        }

        // Text left attachment type
        self.writer.write_i16(174, style.text_left_attachment as i16)?;

        // Text angle type
        self.writer.write_i16(175, style.text_angle_type as i16)?;

        // Text alignment type
        self.writer.write_i16(176, style.text_alignment as i16)?;

        // Text right attachment type
        self.writer.write_i16(178, style.text_right_attachment as i16)?;

        // Text color
        self.write_color_i32(93, style.text_color)?;

        // Text height
        self.writer.write_double(45, style.text_height)?;

        // Enable frame text
        self.writer.write_bool(292, style.text_frame)?;

        // Text always left justify
        self.writer.write_bool(297, style.text_always_left)?;

        // Align space
        self.writer.write_double(46, style.align_space)?;

        // Block content handle
        if let Some(h) = style.block_content_handle {
            self.writer.write_handle(343, h)?;
        }

        // Block content color
        self.write_color_i32(94, style.block_content_color)?;

        // Block content scale (x, y, z)
        self.writer.write_double(47, style.block_content_scale_x)?;
        self.writer.write_double(49, style.block_content_scale_y)?;
        self.writer.write_double(140, style.block_content_scale_z)?;

        // Enable block content scale
        self.writer.write_bool(293, style.enable_block_scale)?;

        // Block content rotation
        self.writer.write_double(141, style.block_content_rotation)?;

        // Enable block content rotation
        self.writer.write_bool(294, style.enable_block_rotation)?;

        // Block content connection type
        self.writer.write_i16(177, style.block_content_connection as i16)?;

        // Scale factor
        self.writer.write_double(142, style.scale_factor)?;

        // Property changed flag
        self.writer.write_bool(295, style.property_changed)?;

        // Is annotative
        self.writer.write_bool(296, style.is_annotative)?;

        // Break gap size
        self.writer.write_double(143, style.break_gap_size)?;

        Ok(())
    }

    /// Write TableStyle object
    fn write_table_style(&mut self, style: &TableStyle) -> Result<()> {
        self.writer.write_string(0, "TABLESTYLE")?;
        self.writer.write_handle(5, style.handle)?;
        self.writer.write_handle(330, style.owner_handle)?;
        self.writer.write_subclass("AcDbTableStyle")?;

        // Version
        self.writer.write_byte(280, style.version as u8)?;

        // Description
        if !style.description.is_empty() {
            self.writer.write_string(3, &style.description)?;
        }

        // Flow direction
        self.writer.write_i16(70, style.flow_direction as i16)?;

        // Flags
        self.writer.write_i16(71, style.flags.bits())?;

        // Horizontal margin
        self.writer.write_double(40, style.horizontal_margin)?;

        // Vertical margin
        self.writer.write_double(41, style.vertical_margin)?;

        // Title suppressed
        self.writer.write_byte(280, style.title_suppressed as u8)?;

        // Header suppressed
        self.writer.write_byte(281, style.header_suppressed as u8)?;

        // Write cell style info for data row
        self.write_table_cell_style("DATA", &style.data_row_style)?;

        // Write cell style info for header row
        self.write_table_cell_style("HEADER", &style.header_row_style)?;

        // Write cell style info for title row
        self.write_table_cell_style("TITLE", &style.title_row_style)?;
        self.write_annotative_xdata(style.annotative)?;

        Ok(())
    }

    /// Helper to write table cell style
    fn write_table_cell_style(&mut self, name: &str, style: &crate::objects::RowCellStyle) -> Result<()> {
        // Cell type indicator - simplified for basic support
        self.writer.write_string(7, &style.text_style_name)?;
        self.writer.write_double(140, style.text_height)?;
        self.writer.write_i16(170, style.alignment as i16)?;
        
        // Text color
        self.write_color_i16(62, style.text_color)?;

        // Fill color
        self.write_color_i16(63, style.fill_color)?;

        // Fill enabled
        self.writer.write_byte(283, style.fill_enabled as u8)?;

        let _ = name; // Name is for future use in extended format
        
        Ok(())
    }

    /// Write Scale object
    fn write_scale(&mut self, scale: &Scale) -> Result<()> {
        self.writer.write_string(0, "SCALE")?;
        self.writer.write_handle(5, scale.handle)?;
        self.writer.write_handle(330, scale.owner_handle)?;
        self.writer.write_subclass("AcDbScale")?;

        // Scale name
        self.writer.write_string(300, &scale.name)?;

        // Paper units
        self.writer.write_double(140, scale.paper_units)?;

        // Drawing units
        self.writer.write_double(141, scale.drawing_units)?;

        // Is unit scale
        self.writer.write_bool(290, scale.is_unit_scale)?;

        Ok(())
    }

    /// Write SortEntitiesTable object
    fn write_sort_entities_table(&mut self, table: &SortEntitiesTable) -> Result<()> {
        self.writer.write_string(0, "SORTENTSTABLE")?;
        self.writer.write_handle(5, table.handle)?;
        self.writer.write_handle(330, table.owner_handle)?;
        self.writer.write_subclass("AcDbSortentsTable")?;

        // Block owner handle
        self.writer.write_handle(330, table.block_owner_handle)?;

        // Allocate new unique sort handles to avoid conflicts with entity handles.
        // Sort entries by original sort_handle to preserve relative draw order,
        // then assign sequential new handles so ascending order is maintained.
        let entries: Vec<_> = table.entries().collect();
        let mut sorted_indices: Vec<usize> = (0..entries.len()).collect();
        sorted_indices.sort_by_key(|&i| entries[i].sort_handle.value());

        let mut new_handles = vec![Handle::NULL; entries.len()];
        for &idx in &sorted_indices {
            new_handles[idx] = self.allocate_handle();
        }

        // Write entries in original order with new unique handles
        for (i, entry) in entries.iter().enumerate() {
            self.writer.write_handle(331, entry.entity_handle)?;
            self.writer.write_handle(5, new_handles[i])?;
        }

        Ok(())
    }

    /// Write DictionaryVariable object
    fn write_dictionary_variable(&mut self, var: &DictionaryVariable) -> Result<()> {
        self.writer.write_string(0, "DICTIONARYVAR")?;
        self.writer.write_handle(5, var.handle)?;

        // Reactor group: owner dictionary is a reactor
        if var.owner_handle != Handle::NULL {
            self.writer.write_string(102, "{ACAD_REACTORS")?;
            self.writer.write_handle(330, var.owner_handle)?;
            self.writer.write_string(102, "}")?;
        }

        self.writer.write_handle(330, var.owner_handle)?;
        self.writer.write_subclass("DictionaryVariables")?;

        // Schema number
        self.writer.write_byte(280, var.schema_number as u8)?;

        // Value
        self.writer.write_string(1, &var.value)?;

        Ok(())
    }

    /// Write a VISUALSTYLE object
    fn write_visualstyle(&mut self, obj: &VisualStyle) -> Result<()> {
        self.writer.write_string(0, "VISUALSTYLE")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbVisualStyle")?;
        self.writer.write_string(2, &obj.description)?;
        self.writer.write_i16(70, obj.style_type)?;
        self.writer.write_i16(71, obj.face_lighting_model)?;
        self.writer.write_i16(72, obj.face_lighting_quality)?;
        self.writer.write_i16(73, obj.face_color_mode)?;
        self.writer.write_i32(90, obj.face_modifier)?;
        self.writer.write_i32(91, obj.edge_model)?;
        self.writer.write_i32(92, obj.edge_style)?;
        if obj.internal_use_only {
            self.writer.write_bool(291, obj.internal_use_only)?;
        }
        Ok(())
    }

    /// Write a MATERIAL object
    fn write_material(&mut self, obj: &Material) -> Result<()> {
        self.writer.write_string(0, "MATERIAL")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbMaterial")?;
        self.writer.write_string(1, &obj.name)?;
        if !obj.description.is_empty() {
            self.writer.write_string(2, &obj.description)?;
        }
        Ok(())
    }

    /// Write an IMAGEDEF_REACTOR object
    fn write_imagedef_reactor(&mut self, obj: &ImageDefinitionReactor) -> Result<()> {
        self.writer.write_string(0, "IMAGEDEF_REACTOR")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbRasterImageDefReactor")?;
        self.writer.write_i32(90, 2)?; // class version
        self.writer.write_handle(330, obj.image_handle)?;
        Ok(())
    }

    /// Write a SPATIAL_FILTER object (block reference / XCLIP clip boundary).
    ///
    /// Inverse of [`read_spatial_filter`]. The two 4×3 transforms are emitted
    /// as 12 code-40 doubles each, in column-major order, after the front/back
    /// clip flags and distances.
    fn write_spatial_filter(&mut self, obj: &SpatialFilter) -> Result<()> {
        self.writer.write_string(0, "SPATIAL_FILTER")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbFilter")?;
        self.writer.write_subclass("AcDbSpatialFilter")?;
        self.writer.write_i16(70, obj.boundary_points.len() as i16)?;
        for p in &obj.boundary_points {
            self.writer.write_point2d(10, *p)?;
        }
        self.writer.write_point3d(210, obj.normal)?;
        self.writer.write_point3d(11, obj.origin)?;
        self.writer.write_i16(71, obj.display_enabled as i16)?;
        self.writer.write_i16(72, obj.front_clip.is_some() as i16)?;
        if let Some(d) = obj.front_clip {
            self.writer.write_double(40, d)?;
        }
        self.writer.write_i16(73, obj.back_clip.is_some() as i16)?;
        if let Some(d) = obj.back_clip {
            self.writer.write_double(41, d)?;
        }
        self.write_matrix_column_major(&obj.inverse_block_transform)?;
        self.write_matrix_column_major(&obj.clip_bound_transform)?;
        Ok(())
    }

    /// Write a 4×3 transform as 12 code-40 doubles in DXF column-major order
    /// (4 columns of 3 rows; the bottom matrix row is implied).
    fn write_matrix_column_major(&mut self, m: &crate::types::Matrix4) -> Result<()> {
        for col in 0..4 {
            for row in 0..3 {
                self.writer.write_double(40, m.m[row][col])?;
            }
        }
        Ok(())
    }

    /// Write a RASTERVARIABLES object
    fn write_raster_variables(&mut self, obj: &RasterVariables) -> Result<()> {
        self.writer.write_string(0, "RASTERVARIABLES")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbRasterVariables")?;
        self.writer.write_i32(90, obj.class_version)?;
        self.writer.write_i16(70, obj.display_image_frame)?;
        self.writer.write_i16(71, obj.image_quality)?;
        self.writer.write_i16(72, obj.units)?;
        Ok(())
    }

    /// Write a DBCOLOR object
    fn write_bookcolor(&mut self, obj: &BookColor) -> Result<()> {
        self.writer.write_string(0, "DBCOLOR")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbColor")?;
        if !obj.color_name.is_empty() {
            self.writer.write_string(1, &obj.color_name)?;
        }
        if !obj.book_name.is_empty() {
            self.writer.write_string(2, &obj.book_name)?;
        }
        Ok(())
    }

    /// Write an ACDBDICTIONARYWDFLT object
    fn write_dict_with_default(&mut self, obj: &DictionaryWithDefault, objects: &std::collections::HashMap<Handle, ObjectType>) -> Result<()> {
        self.writer.write_string(0, "ACDBDICTIONARYWDFLT")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbDictionary")?;
        self.writer.write_i16(281, obj.duplicate_cloning)?;
        for (key, handle) in &obj.entries {
            if !objects.contains_key(handle) {
                continue;
            }
            self.writer.write_string(3, key)?;
            self.writer.write_handle(350, *handle)?;
        }
        self.writer.write_subclass("AcDbDictionaryWithDefault")?;
        self.writer.write_handle(340, obj.default_handle)?;
        Ok(())
    }

    /// Write a WIPEOUTVARIABLES object
    fn write_wipeout_variables(&mut self, obj: &WipeoutVariables) -> Result<()> {
        self.writer.write_string(0, "WIPEOUTVARIABLES")?;
        self.writer.write_handle(5, obj.handle)?;
        self.writer.write_handle(330, obj.owner)?;
        self.writer.write_subclass("AcDbWipeoutVariables")?;
        self.writer.write_i16(70, obj.display_frame)?;
        Ok(())
    }

    /// Write a minimal stub object (handle + owner only)
    fn write_stub_handle_only(&mut self, type_name: &str, handle: Handle, owner: Handle) -> Result<()> {
        self.writer.write_string(0, type_name)?;
        self.writer.write_handle(5, handle)?;
        self.writer.write_handle(330, owner)?;
        Ok(())
    }

    /// Write an unknown object, preserving raw group codes if available.
    fn write_unknown_object(
        &mut self,
        type_name: &str,
        handle: Handle,
        owner: Handle,
        raw_dxf_codes: Option<&[(i32, String)]>,
    ) -> Result<()> {
        if let Some(codes) = raw_dxf_codes {
            self.writer.write_string(0, type_name)?;
            self.writer.write_handle(5, handle)?;
            self.writer.write_handle(330, owner)?;
            for (code, value) in codes {
                self.writer.write_string(*code, value)?;
            }
        }
        // No raw data — skip this object (nothing to write)
        Ok(())
    }

    /// Helper to write color as i32 (true color format)
    fn write_color_i32(&mut self, code: i32, color: Color) -> Result<()> {
        match color {
            Color::ByLayer => self.writer.write_i32(code, 256)?,
            Color::ByBlock => self.writer.write_i32(code, 0)?,
            Color::Index(i) => self.writer.write_i32(code, i as i32)?,
            Color::Rgb { r, g, b } => {
                let rgb = ((r as i32) << 16) | ((g as i32) << 8) | (b as i32);
                self.writer.write_i32(code, rgb)?;
            }
        }
        Ok(())
    }

    /// Helper to write color as i16 (index format)
    fn write_color_i16(&mut self, code: i32, color: Color) -> Result<()> {
        match color {
            Color::ByLayer => self.writer.write_i16(code, 256)?,
            Color::ByBlock => self.writer.write_i16(code, 0)?,
            Color::Index(i) => self.writer.write_i16(code, i as i16)?,
            Color::Rgb { .. } => self.writer.write_i16(code, 7)?, // Default to white/black
        }
        Ok(())
    }

    /// Write extended data (XDATA)
    #[allow(dead_code)]
    fn write_xdata(&mut self, xdata: &ExtendedData) -> Result<()> {
        if xdata.is_empty() {
            return Ok(());
        }

        for record in xdata.records() {
            self.writer.write_string(1001, &record.application_name)?;

            for value in &record.values {
                match value {
                    XDataValue::String(s) => {
                        self.writer.write_string(1000, s)?;
                    }
                    XDataValue::ControlString(s) => {
                        self.writer.write_string(1002, s)?;
                    }
                    XDataValue::LayerName(s) => {
                        self.writer.write_string(1003, s)?;
                    }
                    XDataValue::BinaryData(data) => {
                        self.writer.write_binary(1004, data)?;
                    }
                    XDataValue::Handle(h) => {
                        self.writer.write_handle(1005, *h)?;
                    }
                    XDataValue::Point3D(p) => {
                        self.writer.write_double(1010, p.x)?;
                        self.writer.write_double(1020, p.y)?;
                        self.writer.write_double(1030, p.z)?;
                    }
                    XDataValue::Position3D(p) => {
                        self.writer.write_double(1011, p.x)?;
                        self.writer.write_double(1021, p.y)?;
                        self.writer.write_double(1031, p.z)?;
                    }
                    XDataValue::Displacement3D(p) => {
                        self.writer.write_double(1012, p.x)?;
                        self.writer.write_double(1022, p.y)?;
                        self.writer.write_double(1032, p.z)?;
                    }
                    XDataValue::Direction3D(p) => {
                        self.writer.write_double(1013, p.x)?;
                        self.writer.write_double(1023, p.y)?;
                        self.writer.write_double(1033, p.z)?;
                    }
                    XDataValue::Real(r) => {
                        self.writer.write_double(1040, *r)?;
                    }
                    XDataValue::Distance(d) => {
                        self.writer.write_double(1041, *d)?;
                    }
                    XDataValue::ScaleFactor(s) => {
                        self.writer.write_double(1042, *s)?;
                    }
                    XDataValue::Integer16(i) => {
                        self.writer.write_i16(1070, *i)?;
                    }
                    XDataValue::Integer32(i) => {
                        self.writer.write_i32(1071, *i)?;
                    }
                }
            }
        }

        Ok(())
    }

    /// Write MULTILEADER entity
    fn write_multileader(&mut self, mleader: &crate::entities::MultiLeader, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("MULTILEADER")?;
        self.write_common_entity_data(&mleader.common, owner)?;
        self.writer.write_subclass("AcDbMLeader")?;

        // Class version (hardcoded to 2 for R2010+)
        self.writer.write_i16(270, 2)?;

        // Context data - write the annotation context
        let ctx = &mleader.context;
        self.writer.write_string(300, "CONTEXT_DATA{")?;

        // Scale and position
        self.writer.write_double(40, ctx.scale_factor)?;
        self.writer.write_point3d(10, ctx.content_base_point)?;
        self.writer.write_double(41, ctx.text_height)?;
        self.writer.write_double(140, ctx.arrowhead_size)?;
        self.writer.write_double(145, ctx.landing_gap)?;

        // Text/block attachment types
        self.writer.write_i16(174, ctx.text_left_attachment as i16)?;
        self.writer.write_i16(175, ctx.text_right_attachment as i16)?;
        self.writer.write_i16(176, ctx.block_connection_type as i16)?;
        self.writer.write_i16(177, 0)?;

        // Has text contents
        self.writer.write_bool(290, ctx.has_text_contents)?;

        // Text content fields (conditional)
        if ctx.has_text_contents {
            self.writer.write_string(304, &ctx.text_string)?;
            self.writer.write_point3d(11, ctx.text_normal)?;
            if let Some(h) = ctx.text_style_handle {
                self.writer.write_handle(340, h)?;
            }
            self.writer.write_point3d(12, ctx.text_location)?;
            self.writer.write_point3d(13, ctx.text_direction)?;
            self.writer.write_double(42, ctx.text_rotation)?;
            self.writer.write_double(43, ctx.text_width)?;
            self.writer.write_double(44, ctx.text_boundary_height)?;
            self.writer.write_double(45, ctx.line_spacing_factor)?;
            self.writer.write_i16(170, ctx.line_spacing_style as i16)?;
            self.write_color_i32(90, ctx.text_color)?;
            self.writer.write_i16(171, ctx.text_alignment as i16)?;
            self.writer.write_i16(172, ctx.text_flow_direction as i16)?;
            self.write_color_i32(91, ctx.background_fill_color)?;
            self.writer.write_double(141, ctx.column_width)?;
            self.write_color_i32(92, ctx.background_fill_color)?;
            self.writer.write_bool(291, ctx.background_fill_enabled)?;
            self.writer.write_bool(292, ctx.word_break)?;
            self.writer.write_i16(173, ctx.column_type)?;
            self.writer.write_bool(293, ctx.column_flow_reversed)?;
            self.writer.write_double(142, ctx.column_width)?;
            self.writer.write_double(143, ctx.column_gutter)?;
            self.writer.write_bool(294, ctx.text_height_automatic)?;
        }

        // Background mask
        self.writer.write_bool(295, ctx.background_mask_fill_on)?;

        // Has block contents
        self.writer.write_bool(296, ctx.has_block_contents)?;

        if ctx.has_block_contents {
            if let Some(h) = ctx.block_content_handle {
                self.writer.write_handle(341, h)?;
            }
            self.writer.write_point3d(14, ctx.block_content_normal)?;
            self.writer.write_point3d(15, ctx.block_content_location)?;
            self.writer.write_point3d(16, ctx.block_content_scale)?;
            self.writer.write_double(46, ctx.block_rotation)?;
            self.write_color_i32(93, ctx.block_content_color)?;
        }

        // Transformation base
        self.writer.write_point3d(110, ctx.base_point)?;
        self.writer.write_point3d(111, ctx.base_direction)?;
        self.writer.write_point3d(112, ctx.base_vertical)?;
        self.writer.write_bool(297, ctx.normal_reversed)?;

        // Leader roots
        for root in &ctx.leader_roots {
            self.writer.write_string(302, "LEADER{")?;
            self.writer.write_bool(290, root.content_valid)?;
            self.writer.write_bool(291, root.unknown)?;
            self.writer.write_point3d(10, root.connection_point)?;
            self.writer.write_point3d(11, root.direction)?;
            self.writer.write_i32(90, root.break_points.len() as i32)?;
            for bp in &root.break_points {
                self.writer.write_point3d(12, bp.start_point)?;
                self.writer.write_point3d(13, bp.end_point)?;
            }
            self.writer.write_double(40, root.landing_distance)?;

            // Leader lines
            for line in &root.lines {
                self.writer.write_string(304, "LEADER_LINE{")?;

                // Vertex points
                for pt in &line.points {
                    self.writer.write_point3d(10, *pt)?;
                }

                // Per-line properties
                self.writer.write_i32(91, line.index)?;
                self.writer.write_i16(170, line.path_type as i16)?;
                self.write_color_i32(92, line.line_color)?;
                self.writer.write_handle(340, line.line_type_handle.unwrap_or(Handle::NULL))?;
                self.writer.write_i16(171, line.line_weight.value())?;
                self.writer.write_double(40, line.arrowhead_size)?;
                self.writer.write_handle(341, line.arrowhead_handle.unwrap_or(Handle::NULL))?;
                self.writer.write_i32(93, line.override_flags.bits() as i32)?;
                self.writer.write_i16(271, line.break_info_count as i16)?;

                self.writer.write_string(305, "}")?;
            }

            self.writer.write_string(303, "}")?;
        }

        // Post-leader attachments
        self.writer.write_i16(273, ctx.text_top_attachment as i16)?;
        self.writer.write_i16(272, ctx.text_bottom_attachment as i16)?;

        self.writer.write_string(301, "}")?; // End CONTEXT_DATA

        // Main properties (order must match reference: 340,90,170,91,341,171,290,291,41,342,42,172,343,173,95,174,175,92,292,93,10,43,176,293,271,273,272,295)

        // Style handle
        if let Some(h) = mleader.style_handle {
            self.writer.write_handle(340, h)?;
        }

        // Property override flags
        self.writer.write_i32(90, mleader.property_override_flags.bits() as i32)?;

        // Content type
        self.writer.write_i16(170, mleader.content_type as i16)?;

        // Leader line color
        self.write_color_i32(91, mleader.line_color)?;

        // Leader line type handle (code 341; use ByLayer if null or ByBlock)
        {
            let h = mleader.line_type_handle
                .filter(|h| *h != Handle::NULL && *h != self.byblock_linetype_handle)
                .unwrap_or(self.bylayer_linetype_handle);
            if h != Handle::NULL {
                self.writer.write_handle(341, h)?;
            }
        }

        // Leader line weight
        self.writer.write_i16(171, mleader.line_weight.value())?;

        // Enable landing
        self.writer.write_bool(290, mleader.enable_landing)?;

        // Enable dogleg
        self.writer.write_bool(291, mleader.enable_dogleg)?;

        // Dogleg length
        self.writer.write_double(41, mleader.dogleg_length)?;

        // Text style handle
        if let Some(h) = mleader.text_style_handle {
            self.writer.write_handle(342, h)?;
        }

        // Arrowhead size
        self.writer.write_double(42, mleader.arrowhead_size)?;

        // Text left attachment type
        self.writer.write_i16(172, mleader.text_left_attachment as i16)?;

        // Block content handle
        if let Some(h) = mleader.block_content_handle {
            self.writer.write_handle(343, h)?;
        }

        // Text right attachment type
        self.writer.write_i16(173, mleader.text_right_attachment as i16)?;

        // Text right attachment (i32 duplicate for compatibility)
        self.writer.write_i32(95, mleader.text_right_attachment as i32)?;

        // Text angle type
        self.writer.write_i16(174, mleader.text_angle_type as i16)?;

        // Text alignment type
        self.writer.write_i16(175, mleader.text_alignment as i16)?;

        // Text color
        self.write_color_i32(92, mleader.text_color)?;

        // Text frame
        self.writer.write_bool(292, mleader.text_frame)?;

        // Block content color
        self.write_color_i32(93, mleader.block_content_color)?;

        // Block content scale
        self.writer.write_point3d(10, mleader.block_scale)?;

        // Block content rotation
        self.writer.write_double(43, mleader.block_rotation)?;

        // Block content connection type
        self.writer.write_i16(176, mleader.block_connection_type as i16)?;

        // Enable annotation scale
        self.writer.write_bool(293, mleader.enable_annotation_scale)?;

        // Text align in IPE
        self.writer.write_i16(271, mleader.text_align_in_ipe)?;

        // Text bottom attachment type
        self.writer.write_i16(273, mleader.text_bottom_attachment as i16)?;

        // Text top attachment type
        self.writer.write_i16(272, mleader.text_top_attachment as i16)?;

        // Extend leader to text
        self.writer.write_bool(295, mleader.extend_leader_to_text)?;

        Ok(())
    }

    /// Write MLINE entity
    fn write_mline(&mut self, mline: &crate::entities::MLine, owner: Handle) -> Result<()> {
        
        self.writer.write_entity_type("MLINE")?;
        self.write_common_entity_data(&mline.common, owner)?;
        self.writer.write_subclass("AcDbMline")?;

        // Style name
        self.writer.write_string(2, &mline.style_name)?;

        // Style handle — always write (CAD requires non-null reference)
        let style_h = mline.style_handle.unwrap_or(Handle::NULL);
        self.writer.write_handle(340, style_h)?;

        // Scale factor
        self.writer.write_double(40, mline.scale_factor)?;

        // Justification
        self.writer.write_i16(70, mline.justification as i16)?;

        // Flags
        self.writer.write_i16(71, mline.flags.bits())?;

        // Number of vertices
        self.writer.write_i16(72, mline.vertices.len() as i16)?;

        // Number of style elements
        self.writer.write_i16(73, mline.style_element_count as i16)?;

        // Start point
        self.writer.write_point3d(10, mline.start_point)?;

        // Normal
        self.writer.write_point3d(210, mline.normal)?;

        // Vertices
        for vertex in &mline.vertices {
            // Position
            self.writer.write_point3d(11, vertex.position)?;

            // Direction
            self.writer.write_point3d(12, vertex.direction)?;

            // Miter
            self.writer.write_point3d(13, vertex.miter)?;

            // Segments for each element
            for segment in &vertex.segments {
                // Number of parameters
                self.writer.write_i16(74, segment.parameters.len() as i16)?;

                // Parameters
                for param in &segment.parameters {
                    self.writer.write_double(41, *param)?;
                }

                // Number of area fill parameters
                self.writer.write_i16(75, segment.area_fill_parameters.len() as i16)?;

                // Area fill parameters
                for param in &segment.area_fill_parameters {
                    self.writer.write_double(42, *param)?;
                }
            }
        }

        Ok(())
    }

    /// Write MESH entity
    fn write_mesh(&mut self, mesh: &crate::entities::Mesh, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("MESH")?;
        self.write_common_entity_data(&mesh.common, owner)?;
        self.writer.write_subclass("AcDbSubDMesh")?;

        // Version
        self.writer.write_i16(71, mesh.version)?;

        // Blend crease
        self.writer.write_i16(72, if mesh.blend_crease { 1 } else { 0 })?;

        // Subdivision level
        self.writer.write_i32(91, mesh.subdivision_level)?;

        // Vertex count
        self.writer.write_i32(92, mesh.vertices.len() as i32)?;

        // Vertices
        for v in &mesh.vertices {
            self.writer.write_point3d(10, *v)?;
        }

        // Face count (face list size = count of all indices + face size prefixes)
        let face_list_size: i32 = mesh.faces.iter().map(|f| 1 + f.vertices.len() as i32).sum();
        self.writer.write_i32(93, face_list_size)?;

        // Face data: each face is: vertex_count, v0, v1, v2, ...
        for face in &mesh.faces {
            self.writer.write_i32(90, face.vertices.len() as i32)?;
            for vi in &face.vertices {
                self.writer.write_i32(90, *vi as i32)?;
            }
        }

        // Edge count
        self.writer.write_i32(94, (mesh.edges.len() * 2) as i32)?;

        // Edges: start_index, end_index pairs
        for edge in &mesh.edges {
            self.writer.write_i32(90, edge.start as i32)?;
            self.writer.write_i32(90, edge.end as i32)?;
        }

        // Edge crease count
        let creased_edges: Vec<_> = mesh.edges.iter().enumerate()
            .filter(|(_, e)| e.has_crease())
            .collect();
        self.writer.write_i32(95, creased_edges.len() as i32)?;

        // Edge creases: index, crease_value pairs
        for (idx, edge) in creased_edges {
            self.writer.write_i32(90, idx as i32)?;
            self.writer.write_double(140, edge.crease_value())?;
        }

        Ok(())
    }

    /// Write IMAGE (RasterImage) entity
    fn write_raster_image(&mut self, image: &crate::entities::RasterImage, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("IMAGE")?;
        self.write_common_entity_data(&image.common, owner)?;
        self.writer.write_subclass("AcDbRasterImage")?;

        // Class version
        self.writer.write_i32(90, image.class_version)?;

        // Insertion point
        self.writer.write_point3d(10, image.insertion_point)?;

        // U vector (size of single pixel in world)
        self.writer.write_point3d(11, image.u_vector)?;

        // V vector
        self.writer.write_point3d(12, image.v_vector)?;

        // Image size in pixels
        self.writer.write_double(13, image.size.x)?;
        self.writer.write_double(23, image.size.y)?;

        // Image definition handle
        if let Some(h) = image.definition_handle {
            self.writer.write_handle(340, h)?;
        }

        // Display properties
        self.writer.write_i16(70, image.flags.bits())?;

        // Clipping boundary on
        self.writer.write_byte(280, if image.clipping_enabled { 1 } else { 0 })?;

        // Brightness
        self.writer.write_byte(281, image.brightness)?;

        // Contrast
        self.writer.write_byte(282, image.contrast)?;

        // Fade
        self.writer.write_byte(283, image.fade)?;

        // Image definition reactor handle
        if let Some(h) = image.definition_reactor_handle {
            self.writer.write_handle(360, h)?;
        }

        // Clipping boundary type
        self.writer.write_i16(71, image.clip_boundary.clip_type as i16)?;

        // Number of clip boundary vertices
        self.writer.write_i32(91, image.clip_boundary.vertices.len() as i32)?;

        // Clip boundary vertices
        for v in &image.clip_boundary.vertices {
            self.writer.write_double(14, v.x)?;
            self.writer.write_double(24, v.y)?;
        }

        Ok(())
    }

    /// Write 3DSOLID entity
    fn write_solid3d(&mut self, solid: &Solid3D, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("3DSOLID")?;
        self.write_common_entity_data(&solid.common, owner)?;
        self.writer.write_subclass("AcDbModelerGeometry")?;

        if self.needs_sab() {
            // AC1027+: SAB binary format stored in ACDSDATA section
            self.writer.write_bool(290, false)?;
            self.writer
                .write_string(2, "{00000000-0000-0000-0000-000000000000}")?;

            // Convert SAT to SAB and queue for ACDSDATA section
            self.queue_sab_data(&solid.acis_data, solid.common.handle);
        } else {
            // Pre-AC1027: SAT cipher text inline.
            // Always write version=1 here: the DXF SAT path always outputs
            // SAT text (code-1 groups), even when the source was SAB binary.
            // Writing version=2 would be inconsistent with the SAT output.
            self.writer.write_i16(70, 1)?;
            self.write_acis_data(&solid.acis_data)?;
        }

        self.writer.write_subclass("AcDb3dSolid")?;

        // History handle (always written, 0 = no history)
        let h = solid.history_handle.unwrap_or(Handle::NULL);
        self.writer.write_handle(350, h)?;

        Ok(())
    }

    /// Write REGION entity
    fn write_region(&mut self, region: &Region, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("REGION")?;
        self.write_common_entity_data(&region.common, owner)?;
        self.writer.write_subclass("AcDbModelerGeometry")?;

        if self.needs_sab() {
            self.writer.write_bool(290, false)?;
            self.writer
                .write_string(2, "{00000000-0000-0000-0000-000000000000}")?;
            self.queue_sab_data(&region.acis_data, region.common.handle);
        } else {
            self.writer.write_i16(70, 1)?;
            self.write_acis_data(&region.acis_data)?;
        }

        Ok(())
    }

    /// Write BODY entity
    fn write_body(&mut self, body: &Body, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("BODY")?;
        self.write_common_entity_data(&body.common, owner)?;
        self.writer.write_subclass("AcDbModelerGeometry")?;

        if self.needs_sab() {
            self.writer.write_bool(290, false)?;
            self.writer
                .write_string(2, "{00000000-0000-0000-0000-000000000000}")?;
            self.queue_sab_data(&body.acis_data, body.common.handle);
        } else {
            self.writer.write_i16(70, 1)?;
            self.write_acis_data(&body.acis_data)?;
        }

        Ok(())
    }

    /// Write ACIS data (shared by Solid3D, Region, Body)
    ///
    /// SAT text is split by newlines; each line becomes a separate DXF
    /// group-code entry using group code 1.  Lines longer than 255
    /// characters are subdivided into 255-char sub-chunks: the first
    /// sub-chunk uses group code 1 and continuation sub-chunks use
    /// group code 3.
    ///
    /// When only SAB binary data is present (no SAT text), attempts to
    /// convert via `SabReader` before falling back to an empty entry.
    /// Also downgrades ACIS v600+ SAT text to v400 record layout.
    fn write_acis_data(&mut self, acis: &AcisData) -> Result<()> {
        let converted;
        let data: &str = if acis.sat_data.is_empty() && !acis.sab_data.is_empty() {
            // SAB binary only — convert via SabReader.
            match crate::entities::acis::SabReader::read(&acis.sab_data) {
                Ok(mut doc) => {
                    let source_major = doc.header.version.major;
                    doc.header.version = crate::entities::acis::SatVersion::V4_0;
                    doc.header.num_records = doc.records.len();
                    doc.records.retain(|r| r.entity_type != "asmheader");
                    if source_major >= 6 {
                        crate::entities::acis::downgrade_records_to_v400(&mut doc.records);
                    }
                    converted = doc.to_sat_string();
                    &converted
                }
                Err(_) => "",
            }
        } else if !acis.sat_data.is_empty() {
            // SAT text present — parse and downgrade v600+ records to v400.
            // Some SAT data has a v400 version header but v600 record layouts
            // (e.g. ACIS Builder 6.00). detect and fix based on actual content.
            match crate::entities::acis::SatDocument::parse(&acis.sat_data) {
                Ok(mut doc) => {
                    doc.header.version = crate::entities::acis::SatVersion::V4_0;
                    doc.header.num_records = doc.records.len();
                    doc.records.retain(|r| r.entity_type != "asmheader");
                    crate::entities::acis::downgrade_records_to_v400(&mut doc.records);
                    converted = doc.to_sat_string();
                    &converted
                }
                Err(_) => &acis.sat_data,
            }
        } else {
            &acis.sat_data
        };

        if data.is_empty() {
            self.writer.write_string(1, "")?;
            return Ok(());
        }

        // Append the terminator — internal sat_data never contains it.
        let mut full = AcisData::strip_sat_terminator(data);
        full.push_str("End-of-ACIS-data\n");

        // Version 1: apply the DXF character cipher to SAT text.
        // SAB-converted data is always treated as Version1 for DXF output.
        let use_version1_cipher = acis.version == AcisVersion::Version1
            || (acis.sat_data.is_empty() && !acis.sab_data.is_empty());
        let encoded = if use_version1_cipher {
            AcisData::encode_sat(&full)
        } else {
            full
        };

        let mut any_written = false;
        for line in encoded.lines() {
            if line.len() <= 255 {
                // Whole line fits in one chunk → group code 1
                self.writer.write_string(1, line)?;
            } else {
                // Split into 255-char sub-chunks:
                // first sub-chunk → gc 1, continuations → gc 3
                let mut remaining = line;
                let mut first = true;
                while !remaining.is_empty() {
                    let end = remaining.len().min(255);
                    let (chunk, rest) = remaining.split_at(end);
                    if first {
                        self.writer.write_string(1, chunk)?;
                        first = false;
                    } else {
                        self.writer.write_string(3, chunk)?;
                    }
                    remaining = rest;
                }
            }
            any_written = true;
        }

        if !any_written {
            self.writer.write_string(1, "")?;
        }

        Ok(())
    }

    /// Write ACAD_TABLE entity
    fn write_acad_table(&mut self, table: &table::Table, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("ACAD_TABLE")?;
        self.write_common_entity_data(&table.common, owner)?;
        self.writer.write_subclass("AcDbBlockReference")?;

        // Block record handle
        if let Some(h) = table.block_record_handle {
            self.writer.write_handle(2, h)?;
        }

        // Insertion point
        self.writer.write_point3d(10, table.insertion_point)?;

        self.writer.write_subclass("AcDbTable")?;

        // Table style handle
        if let Some(h) = table.table_style_handle {
            self.writer.write_handle(342, h)?;
        }

        // Data version
        self.writer.write_byte(280, table.data_version as u8)?;

        // Horizontal direction
        self.writer.write_point3d(11, table.horizontal_direction)?;

        // Number of rows
        self.writer.write_i32(91, table.rows.len() as i32)?;

        // Number of columns
        self.writer.write_i32(92, table.columns.len() as i32)?;

        // Override flags
        let mut override_flags = 0i32;
        if table.override_flag { override_flags |= 1; }
        if table.override_border_color { override_flags |= 2; }
        if table.override_border_line_weight { override_flags |= 4; }
        if table.override_border_visibility { override_flags |= 8; }
        self.writer.write_i32(93, override_flags)?;

        // Row heights
        for row in &table.rows {
            self.writer.write_double(141, row.height)?;
        }

        // Column widths
        for col in &table.columns {
            self.writer.write_double(142, col.width)?;
        }

        // Write cells
        for row in &table.rows {
            for cell in &row.cells {
                self.write_table_cell(cell)?;
            }
        }

        // Break options
        self.writer.write_i32(94, table.break_options.bits() as i32)?;
        self.writer.write_i32(95, table.break_flow_direction as i32)?;
        self.writer.write_double(143, table.break_spacing)?;

        Ok(())
    }

    /// Write table cell data
    fn write_table_cell(&mut self, cell: &TableCell) -> Result<()> {
        // Cell type
        self.writer.write_i16(171, cell.cell_type as i16)?;

        // Cell state flags
        self.writer.write_i16(172, cell.state.bits() as i16)?;

        // Cell flags
        self.writer.write_i16(173, cell.flag as i16)?;

        // Merged dimensions
        self.writer.write_i16(174, cell.merged as i16)?;
        self.writer.write_i16(175, cell.merge_width as i16)?;
        self.writer.write_i16(176, cell.merge_height as i16)?;

        // Virtual edge flag
        self.writer.write_i16(177, cell.virtual_edge)?;

        // Rotation
        self.writer.write_double(144, cell.rotation)?;

        // Contents count
        self.writer.write_i16(179, cell.contents.len() as i16)?;

        // Write cell contents
        for content in &cell.contents {
            self.writer.write_i16(170, content.content_type as i16)?;

            // Write value based on type
            match content.value.value_type {
                CellValueType::String => {
                    self.writer.write_string(1, &content.value.text)?;
                }
                CellValueType::Double => {
                    self.writer.write_double(140, content.value.numeric_value)?;
                }
                CellValueType::Long => {
                    self.writer.write_i32(90, content.value.numeric_value as i32)?;
                }
                _ => {}
            }

            // Format string
            if !content.value.format.is_empty() {
                self.writer.write_string(300, &content.value.format)?;
            }

            // Block handle
            if let Some(h) = content.block_handle {
                self.writer.write_handle(340, h)?;
            }
        }

        // Cell style
        if let Some(ref style) = cell.style {
            self.writer.write_color(62, style.content_color)?;
            self.writer.write_double(140, style.text_height)?;
            self.writer.write_double(144, style.rotation)?;
            self.writer.write_i16(170, style.alignment as i16)?;
        }

        Ok(())
    }

    /// Write a Tolerance entity
    fn write_tolerance(&mut self, tolerance: &Tolerance, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("TOLERANCE")?;
        self.write_common_entity_data(&tolerance.common, owner)?;
        self.writer.write_subclass("AcDbFcf")?;

        // Dimension style name
        self.writer.write_string(3, &tolerance.dimension_style_name)?;

        // Insertion point
        self.writer.write_double(10, tolerance.insertion_point.x)?;
        self.writer.write_double(20, tolerance.insertion_point.y)?;
        self.writer.write_double(30, tolerance.insertion_point.z)?;

        // Normal vector
        self.writer.write_double(210, tolerance.normal.x)?;
        self.writer.write_double(220, tolerance.normal.y)?;
        self.writer.write_double(230, tolerance.normal.z)?;

        // Direction vector
        self.writer.write_double(11, tolerance.direction.x)?;
        self.writer.write_double(21, tolerance.direction.y)?;
        self.writer.write_double(31, tolerance.direction.z)?;

        // Tolerance text
        self.writer.write_string(1, &tolerance.text)?;

        Ok(())
    }

    /// Write a PolyfaceMesh entity
    fn write_polyface_mesh(&mut self, mesh: &PolyfaceMesh, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("POLYLINE")?;
        self.write_common_entity_data(&mesh.common, owner)?;
        self.writer.write_subclass("AcDbPolyFaceMesh")?;

        // Entities follow flag (VERTEX records follow)
        self.writer.write_i16(66, 1)?;

        // Dummy point with elevation (ACadSharp pattern)
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, mesh.elevation)?;
        
        // Polyline flags (64 = polyface mesh) - MUST be before 71/72
        self.writer.write_i16(70, mesh.flags.bits())?;
        
        // Vertex count - MUST come before smooth surface type
        self.writer.write_i16(71, mesh.vertex_count() as i16)?;
        // Face count - MUST come before smooth surface type  
        self.writer.write_i16(72, mesh.face_count() as i16)?;

        // Write vertices with proper subclass markers
        for vertex in mesh.vertices.iter() {
            let vertex_handle = if vertex.common.handle.is_null() {
                self.allocate_handle()
            } else {
                vertex.common.handle
            };
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, vertex_handle)?;
            self.writer.write_handle(330, mesh.common.handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &vertex.common.layer)?;
            self.writer.write_subclass("AcDbVertex")?;
            self.writer.write_subclass("AcDbPolyFaceMeshVertex")?;

            self.writer.write_double(10, vertex.location.x)?;
            self.writer.write_double(20, vertex.location.y)?;
            self.writer.write_double(30, vertex.location.z)?;

            let flags = vertex.flags | PolyfaceVertexFlags::POLYGON_MESH | PolyfaceVertexFlags::POLYFACE_MESH;
            self.writer.write_i16(70, flags.bits())?;
        }

        // Write faces with proper subclass markers
        for face in mesh.faces.iter() {
            let face_handle = if face.common.handle.is_null() {
                self.allocate_handle()
            } else {
                face.common.handle
            };
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, face_handle)?;
            self.writer.write_handle(330, mesh.common.handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &face.common.layer)?;
            if let Some(c) = face.color {
                if let Some(tc) = c.to_true_color_value() {
                    self.writer.write_i32(420, tc)?;
                } else if let Some(idx) = c.index() {
                    self.writer.write_i16(62, idx as i16)?;
                }
            }
            self.writer.write_subclass("AcDbFaceRecord")?;

            // Dummy position
            self.writer.write_double(10, 0.0)?;
            self.writer.write_double(20, 0.0)?;
            self.writer.write_double(30, 0.0)?;

            let flags = face.flags | PolyfaceVertexFlags::POLYFACE_MESH;
            self.writer.write_i16(70, flags.bits())?; // Face record flag

            // Vertex indices (preserve sign for edge visibility)
            self.writer.write_i16(71, face.index1)?;
            self.writer.write_i16(72, face.index2)?;
            self.writer.write_i16(73, face.index3)?;
            if face.index4 != 0 {
                self.writer.write_i16(74, face.index4)?;
            }
        }

        // Write SEQEND with AcDbEntity subclass
        self.writer.write_entity_type("SEQEND")?;
        let seqend_handle = mesh.seqend_handle.unwrap_or_else(|| self.allocate_handle());
        self.writer.write_handle(5, seqend_handle)?;
        self.writer.write_handle(330, mesh.common.handle)?;
        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &mesh.common.layer)?;

        Ok(())
    }

    /// Write a Wipeout entity
    fn write_wipeout(&mut self, wipeout: &Wipeout, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("WIPEOUT")?;
        self.write_common_entity_data(&wipeout.common, owner)?;
        self.writer.write_subclass("AcDbWipeout")?;

        // Class version
        self.writer.write_i32(90, wipeout.class_version)?;

        // Insertion point
        self.writer.write_double(10, wipeout.insertion_point.x)?;
        self.writer.write_double(20, wipeout.insertion_point.y)?;
        self.writer.write_double(30, wipeout.insertion_point.z)?;

        // U-vector
        self.writer.write_double(11, wipeout.u_vector.x)?;
        self.writer.write_double(21, wipeout.u_vector.y)?;
        self.writer.write_double(31, wipeout.u_vector.z)?;

        // V-vector
        self.writer.write_double(12, wipeout.v_vector.x)?;
        self.writer.write_double(22, wipeout.v_vector.y)?;
        self.writer.write_double(32, wipeout.v_vector.z)?;

        // Size
        self.writer.write_double(13, wipeout.size.x)?;
        self.writer.write_double(23, wipeout.size.y)?;

        // Display flags
        self.writer.write_i16(70, wipeout.flags.bits())?;

        // Clipping
        self.writer.write_byte(280, if wipeout.clipping_enabled { 1 } else { 0 })?;
        self.writer.write_byte(281, wipeout.brightness)?;
        self.writer.write_byte(282, wipeout.contrast)?;
        self.writer.write_byte(283, wipeout.fade)?;

        // Clip boundary type
        self.writer.write_i16(71, wipeout.clip_type as i16)?;

        // Clip boundary count
        self.writer.write_i32(91, wipeout.clip_boundary_vertices.len() as i32)?;

        // Clip boundary vertices
        for v in &wipeout.clip_boundary_vertices {
            self.writer.write_double(14, v.x)?;
            self.writer.write_double(24, v.y)?;
        }

        Ok(())
    }

    /// Write a Shape entity
    fn write_shape(&mut self, shape: &Shape, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("SHAPE")?;
        self.write_common_entity_data(&shape.common, owner)?;
        self.writer.write_subclass("AcDbShape")?;

        // Thickness
        if shape.thickness.abs() > 1e-10 {
            self.writer.write_double(39, shape.thickness)?;
        }

        // Insertion point
        self.writer.write_double(10, shape.insertion_point.x)?;
        self.writer.write_double(20, shape.insertion_point.y)?;
        self.writer.write_double(30, shape.insertion_point.z)?;

        // Size
        self.writer.write_double(40, shape.size)?;

        // Shape name
        self.writer.write_string(2, &shape.shape_name)?;

        // Rotation
        if shape.rotation.abs() > 1e-10 {
            self.writer.write_double(50, shape.rotation.to_degrees())?;
        }

        // Relative X scale
        if (shape.relative_x_scale - 1.0).abs() > 1e-10 {
            self.writer.write_double(41, shape.relative_x_scale)?;
        }

        // Oblique angle
        if shape.oblique_angle.abs() > 1e-10 {
            self.writer.write_double(51, shape.oblique_angle.to_degrees())?;
        }

        // Normal
        if shape.has_custom_normal() {
            self.writer.write_double(210, shape.normal.x)?;
            self.writer.write_double(220, shape.normal.y)?;
            self.writer.write_double(230, shape.normal.z)?;
        }

        Ok(())
    }

    /// Write an Underlay entity (PDF, DWF, or DGN)
    fn write_underlay(&mut self, underlay: &Underlay, owner: Handle) -> Result<()> {
        self.writer.write_entity_type(underlay.entity_name())?;
        self.write_common_entity_data(&underlay.common, owner)?;
        self.writer.write_subclass("AcDbUnderlayReference")?;

        // Definition handle
        self.writer.write_handle(340, underlay.definition_handle)?;

        // Insertion point
        self.writer.write_double(10, underlay.insertion_point.x)?;
        self.writer.write_double(20, underlay.insertion_point.y)?;
        self.writer.write_double(30, underlay.insertion_point.z)?;

        // Scale factors
        self.writer.write_double(41, underlay.x_scale)?;
        self.writer.write_double(42, underlay.y_scale)?;
        self.writer.write_double(43, underlay.z_scale)?;

        // Rotation
        self.writer.write_double(50, underlay.rotation.to_degrees())?;

        // Normal
        self.writer.write_double(210, underlay.normal.x)?;
        self.writer.write_double(220, underlay.normal.y)?;
        self.writer.write_double(230, underlay.normal.z)?;

        // Flags
        self.writer.write_byte(280, underlay.flags.bits())?;

        // Contrast
        self.writer.write_byte(281, underlay.contrast)?;

        // Fade
        self.writer.write_byte(282, underlay.fade)?;

        // Clip boundary vertices count
        self.writer.write_i32(91, underlay.clip_boundary_vertices.len() as i32)?;

        // Clip boundary vertices
        for v in &underlay.clip_boundary_vertices {
            self.writer.write_double(11, v.x)?;
            self.writer.write_double(21, v.y)?;
        }

        Ok(())
    }

    /// Write SEQEND entity (end-of-sequence marker)
    fn write_seqend(&mut self, seqend: &Seqend, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("SEQEND")?;
        self.write_common_entity_data(&seqend.common, owner)?;
        Ok(())
    }

    /// Write OLE2FRAME entity
    fn write_ole2frame(&mut self, ole: &Ole2Frame, owner: Handle) -> Result<()> {
        self.writer.write_entity_type("OLE2FRAME")?;
        self.write_common_entity_data(&ole.common, owner)?;
        self.writer.write_subclass("AcDbOle2Frame")?;
        self.writer.write_i16(70, ole.version)?;
        if !ole.source_application.is_empty() {
            self.writer.write_string(3, &ole.source_application)?;
        }
        self.writer.write_double(10, ole.upper_left_corner.x)?;
        self.writer.write_double(20, ole.upper_left_corner.y)?;
        self.writer.write_double(30, ole.upper_left_corner.z)?;
        self.writer.write_double(11, ole.lower_right_corner.x)?;
        self.writer.write_double(21, ole.lower_right_corner.y)?;
        self.writer.write_double(31, ole.lower_right_corner.z)?;
        self.writer.write_i16(71, ole.ole_object_type as i16)?;
        self.writer.write_i16(72, if ole.is_paper_space { 1 } else { 0 })?;
        self.writer.write_i16(73, 3)?; // undocumented hardcoded value per ACadSharp
        if !ole.binary_data.is_empty() {
            self.writer.write_i32(90, ole.binary_data.len() as i32)?;
            // Write binary data in 127-byte hex chunks (code 310)
            for chunk in ole.binary_data.chunks(127) {
                let hex: String = chunk.iter().map(|b| format!("{:02X}", b)).collect();
                self.writer.write_string(310, &hex)?;
            }
        }
        self.writer.write_string(1, "OLE")?;
        Ok(())
    }

    /// Write PolygonMesh entity (POLYLINE with flag bit 16)
    fn write_polygon_mesh(&mut self, mesh: &PolygonMeshEntity, owner: Handle) -> Result<()> {
        use crate::entities::polygon_mesh::PolygonMeshFlags;

        self.writer.write_entity_type("POLYLINE")?;
        self.write_common_entity_data(&mesh.common, owner)?;
        self.writer.write_subclass("AcDbPolygonMesh")?;

        // Entities follow flag (VERTEX records follow)
        self.writer.write_i16(66, 1)?;

        // Dummy origin point (required by DXF spec for POLYLINE entity)
        self.writer.write_double(10, 0.0)?;
        self.writer.write_double(20, 0.0)?;
        self.writer.write_double(30, 0.0)?;

        // Ensure PolygonMesh flag (16) is always set
        let flags = mesh.flags | PolygonMeshFlags::POLYGON_MESH;
        self.writer.write_i16(70, flags.bits())?;
        self.writer.write_i16(71, mesh.m_vertex_count)?;
        self.writer.write_i16(72, mesh.n_vertex_count)?;
        self.writer.write_i16(73, mesh.m_smooth_density)?;
        self.writer.write_i16(74, mesh.n_smooth_density)?;
        self.writer.write_i16(75, mesh.smooth_type as i16)?;
        if mesh.normal != Vector3::UNIT_Z {
            self.writer.write_double(210, mesh.normal.x)?;
            self.writer.write_double(220, mesh.normal.y)?;
            self.writer.write_double(230, mesh.normal.z)?;
        }

        // VERTEX and SEQEND are owned by the mesh entity
        let mesh_handle = mesh.common.handle;

        // Write vertices with proper subclass markers
        for vertex in &mesh.vertices {
            let vertex_handle = if vertex.common.handle.is_null() {
                self.allocate_handle()
            } else {
                vertex.common.handle
            };
            self.writer.write_entity_type("VERTEX")?;
            self.writer.write_handle(5, vertex_handle)?;
            self.writer.write_handle(330, mesh_handle)?;
            self.writer.write_subclass("AcDbEntity")?;
            self.writer.write_string(8, &vertex.common.layer)?;
            // Propagate parent color to vertex so CAD doesn't flag mismatch
            if mesh.common.color != Color::ByLayer {
                self.writer.write_color(62, mesh.common.color)?;
            }
            self.writer.write_subclass("AcDbVertex")?;
            self.writer.write_subclass("AcDbPolygonMeshVertex")?;
            self.writer.write_point3d(10, vertex.location)?;
            if vertex.flags != 0 {
                self.writer.write_i16(70, vertex.flags)?;
            }
        }

        // Write SEQEND
        let seqend_handle = self.allocate_handle();
        self.writer.write_entity_type("SEQEND")?;
        self.writer.write_handle(5, seqend_handle)?;
        self.writer.write_handle(330, mesh_handle)?;
        self.writer.write_subclass("AcDbEntity")?;
        self.writer.write_string(8, &mesh.common.layer)?;

        Ok(())
    }

    /// Convert ACIS data to SAB binary and queue for ACDSDATA section.
    fn queue_sab_data(&mut self, acis: &AcisData, entity_handle: Handle) {
        if acis.is_binary && !acis.sab_data.is_empty() {
            // Already have SAB binary data, use it directly
            self.sab_entries
                .push((entity_handle, acis.sab_data.clone()));
        } else if !acis.sat_data.is_empty() {
            // Convert SAT text to SAB binary via SatDocument.
            // Strip non-geometry entities (attributes, refinement, etc.)
            // which cause ACIS "NOT THAT KIND OF CLASS" errors in SAB.
            if let Ok(mut sat_doc) =
                crate::entities::acis::SatDocument::parse(&acis.sat_data)
            {
                sat_doc.strip_for_sab();
                let sab = crate::entities::acis::SabWriter::write(&sat_doc);
                self.sab_entries.push((entity_handle, sab));
            }
        }
    }

    /// Write the ACDSDATA section (AC1027+ only, for SAB binary ACIS data).
    ///
    /// This section stores ACIS SAB binary data for 3DSOLID, REGION, and BODY
    /// entities when the DXF version is AC1027 (R2013) or later.
    pub fn write_acdsdata(&mut self) -> Result<()> {
        if self.sab_entries.is_empty() {
            return Ok(());
        }

        self.writer.write_section_start("ACDSDATA")?;

        // Section-level header
        self.writer.write_i16(70, 2)?;
        self.writer.write_i16(71, 2)?;

        // Schema 0: AcDb_Thumbnail_Schema (standard boilerplate)
        self.write_acds_thumbnail_schema()?;

        // Schema 1: AcDb3DSolid_ASM_Data (for SAB data)
        self.write_acds_asm_schema()?;

        // Schemas 2-5: Standard infrastructure schemas
        self.write_acds_infrastructure_schemas()?;

        // ACDSRECORD entries (one per entity with SAB data)
        // Take entries from self to avoid borrow issues
        let entries = std::mem::take(&mut self.sab_entries);
        for (entity_handle, sab_data) in &entries {
            self.write_acds_record(*entity_handle, sab_data)?;
        }
        self.sab_entries = entries;

        self.writer.write_section_end()?;
        Ok(())
    }

    fn write_acds_thumbnail_schema(&mut self) -> Result<()> {
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 0)?;
        self.writer.write_string(1, "AcDb_Thumbnail_Schema")?;
        self.writer.write_string(2, "AcDbDs::ID")?;
        self.writer.write_byte(280, 10)?;
        self.writer.write_i32(91, 8)?;
        self.writer.write_string(2, "Thumbnail_Data")?;
        self.writer.write_byte(280, 15)?;
        self.writer.write_i32(91, 0)?;

        // Schema records
        self.write_acds_schema_records(0)?;
        Ok(())
    }

    fn write_acds_asm_schema(&mut self) -> Result<()> {
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 1)?;
        self.writer.write_string(1, "AcDb3DSolid_ASM_Data")?;
        self.writer.write_string(2, "AcDbDs::ID")?;
        self.writer.write_byte(280, 10)?;
        self.writer.write_i32(91, 8)?;
        self.writer.write_string(2, "ASM_Data")?;
        self.writer.write_byte(280, 15)?;
        self.writer.write_i32(91, 0)?;

        // Schema records
        self.write_acds_schema_records(1)?;
        Ok(())
    }

    fn write_acds_schema_records(&mut self, schema_id: i32) -> Result<()> {
        // TreatedAsObjectData record
        self.writer.write_string(101, "ACDSRECORD")?;
        self.writer.write_i32(95, schema_id)?;
        self.writer.write_i32(90, 2)?;
        self.writer.write_string(2, "AcDbDs::TreatedAsObjectData")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_bool(291, true)?;

        // Legacy record
        self.writer.write_string(101, "ACDSRECORD")?;
        self.writer.write_i32(95, schema_id)?;
        self.writer.write_i32(90, 3)?;
        self.writer.write_string(2, "AcDbDs::Legacy")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_bool(291, true)?;

        // Indexable record
        self.writer.write_string(101, "ACDSRECORD")?;
        self.writer.write_string(1, "AcDbDs::ID")?;
        self.writer.write_i32(90, 4)?;
        self.writer.write_string(2, "AcDs:Indexable")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_bool(291, true)?;

        // HandleAttribute record
        self.writer.write_string(101, "ACDSRECORD")?;
        self.writer.write_string(1, "AcDbDs::ID")?;
        self.writer.write_i32(90, 5)?;
        self.writer.write_string(2, "AcDbDs::HandleAttribute")?;
        self.writer.write_byte(280, 7)?;
        self.writer.write_i16(282, 1)?;

        Ok(())
    }

    fn write_acds_infrastructure_schemas(&mut self) -> Result<()> {
        // Schema 2: TreatedAsObjectDataSchema
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 2)?;
        self.writer
            .write_string(1, "AcDbDs::TreatedAsObjectDataSchema")?;
        self.writer
            .write_string(2, "AcDbDs::TreatedAsObjectData")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_i32(91, 0)?;

        // Schema 3: LegacySchema
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 3)?;
        self.writer.write_string(1, "AcDbDs::LegacySchema")?;
        self.writer.write_string(2, "AcDbDs::Legacy")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_i32(91, 0)?;

        // Schema 4: IndexedPropertySchema
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 4)?;
        self.writer
            .write_string(1, "AcDbDs::IndexedPropertySchema")?;
        self.writer.write_string(2, "AcDs:Indexable")?;
        self.writer.write_byte(280, 1)?;
        self.writer.write_i32(91, 0)?;

        // Schema 5: HandleAttributeSchema
        self.writer.write_string(0, "ACDSSCHEMA")?;
        self.writer.write_i32(90, 5)?;
        self.writer
            .write_string(1, "AcDbDs::HandleAttributeSchema")?;
        self.writer.write_string(2, "AcDbDs::HandleAttribute")?;
        self.writer.write_byte(280, 7)?;
        self.writer.write_i32(91, 1)?;
        self.writer.write_i16(284, 1)?;

        Ok(())
    }

    fn write_acds_record(
        &mut self,
        entity_handle: Handle,
        sab_data: &[u8],
    ) -> Result<()> {
        self.writer.write_string(0, "ACDSRECORD")?;

        // Schema reference (1 = AcDb3DSolid_ASM_Data)
        self.writer.write_i32(90, 1)?;

        // Entity handle reference
        self.writer.write_string(2, "AcDbDs::ID")?;
        self.writer.write_byte(280, 10)?;
        self.writer.write_handle(320, entity_handle)?;

        // ASM_Data field with SAB binary
        self.writer.write_string(2, "ASM_Data")?;
        self.writer.write_byte(280, 15)?;

        // Total byte count
        self.writer.write_i32(94, sab_data.len() as i32)?;

        // Write SAB data in 127-byte chunks as gc=310
        for chunk in sab_data.chunks(127) {
            self.writer.write_binary(310, chunk)?;
        }

        Ok(())
    }
}

/// Helper to extract invisible edge bits
fn get_invisible_edge_bits(flags: &InvisibleEdgeFlags) -> u8 {
    let mut bits = 0u8;
    if flags.is_first_invisible() { bits |= 1; }
    if flags.is_second_invisible() { bits |= 2; }
    if flags.is_third_invisible() { bits |= 4; }
    if flags.is_fourth_invisible() { bits |= 8; }
    bits
}

/// Helper to extract boundary path flag bits
fn get_boundary_path_bits(flags: &BoundaryPathFlags) -> u32 {
    let mut bits = 0u32;
    if flags.is_external() { bits |= 1; }
    if flags.is_polyline() { bits |= 2; }
    if flags.is_derived() { bits |= 4; }
    bits
}

