use super::*;

impl OpenCADStudio {
    pub(super) fn dispatch_draw(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
        match cmd {
            // ── Draw commands ──────────────────────────────────────────────
            "LINE" => {
                use crate::modules::draw::draw::line::LineCommand;
                let new_cmd = LineCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "MLINE" => {
                use crate::modules::draw::draw::mline::MlineCommand;
                let style = self.tabs[i].scene.document.header.multiline_style.clone();
                let cmd_obj = MlineCommand::with_style(style);
                self.command_line.push_info(&cmd_obj.prompt());
                self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
            }

            cmd if cmd == "WIPEOUT" || cmd == "WO" || cmd.starts_with("WIPEOUT ") => {
                use crate::modules::draw::draw::wipeout::WipeoutCommand;
                let args = cmd
                    .split_once(' ')
                    .map(|(_, r)| r.trim().to_uppercase())
                    .unwrap_or_default();
                let wo_cmd = if args == "P" || args == "POLYGONAL" {
                    WipeoutCommand::new_polygonal()
                } else {
                    WipeoutCommand::new_rectangular()
                };
                self.command_line.push_info(&wo_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(wo_cmd));
            }

            cmd if cmd == "IMAGE" || cmd == "IMAGEATTACH" || cmd == "IM" => {
                return Some(Task::done(Message::ImagePick));
            }

            "REVCLOUD" => {
                use crate::modules::draw::draw::revcloud::RevCloudCommand;
                let cmd = RevCloudCommand::new();
                self.command_line.push_info(&cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(cmd));
            }

            "ATTDEF" => {
                use crate::modules::draw::draw::attdef::AttdefCommand;
                let cmd = AttdefCommand::new();
                self.command_line.push_info(&cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(cmd));
            }

            // Command-line attribute editing on selected Insert entities. Bare
            // ATTEDIT and the ATE alias launch the interactive editor instead
            // (see the ATTEDIT arm in the inquiry family); the dash form is the
            // command-line entry point.
            // Usage:
            //   -ATTEDIT          — list all attributes on selected Insert(s)
            //   ATTEDIT <tag> <v> — quick-set attribute <tag> to <v>
            cmd if cmd.starts_with("ATTEDIT ")
                || cmd == "-ATTEDIT"
                || cmd.starts_with("-ATTEDIT ") =>
            {
                let rest = cmd
                    .trim_start_matches("-ATTEDIT")
                    .trim_start_matches("ATTEDIT")
                    .trim();
                let parts: Vec<&str> = rest.splitn(2, char::is_whitespace).collect();
                let selected_handles: Vec<acadrust::Handle> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .iter()
                    .map(|(h, _)| *h)
                    .collect();
                if selected_handles.is_empty() {
                    self.command_line
                        .push_error("ATTEDIT: select an Insert entity first.");
                } else {
                    let mut found_any = false;
                    for sh in &selected_handles {
                        if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i]
                            .scene
                            .document
                            .entities()
                            .find(|e| e.common().handle == *sh)
                        {
                            found_any = true;
                            if rest.is_empty() {
                                // List attributes.
                                if ins.attributes.is_empty() {
                                    self.command_line.push_output(&format!(
                                        "  Insert {:x}: no attributes.",
                                        sh.value()
                                    ));
                                } else {
                                    for attr in &ins.attributes {
                                        self.command_line.push_output(&format!(
                                            "  [{tag}] = {val}",
                                            tag = attr.tag,
                                            val = attr.get_value()
                                        ));
                                    }
                                }
                            }
                        }
                    }
                    if !found_any {
                        self.command_line
                            .push_error("ATTEDIT: no Insert entities in selection.");
                    }
                    // If tag + value supplied, mutate attributes.
                    if parts.len() == 2 && !parts[0].is_empty() {
                        let tag_up = parts[0].to_uppercase();
                        let new_val = parts[1];
                        let mut changed = 0usize;
                        self.push_undo_snapshot(i, "ATTEDIT");
                        for sh in &selected_handles {
                            if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i]
                                .scene
                                .document
                                .entities_mut()
                                .find(|e| e.common().handle == *sh)
                            {
                                for attr in &mut ins.attributes {
                                    if attr.tag.to_uppercase() == tag_up {
                                        attr.set_value(new_val);
                                        changed += 1;
                                    }
                                }
                            }
                        }
                        if changed > 0 {
                            self.tabs[i].dirty = true;
                            self.command_line.push_output(&format!(
                                "ATTEDIT: updated {changed} attribute(s) [{tag_up}] = {new_val}."
                            ));
                        } else {
                            self.command_line.push_error(&format!(
                                "ATTEDIT: tag '{tag_up}' not found in selection."
                            ));
                        }
                    }
                }
            }

            // ATTDISP — control attribute display visibility.
            // ATTDISP ON   — make all AttributeDefinitions visible
            // ATTDISP OFF  — make all AttributeDefinitions invisible
            // ATTDISP NORMAL — restore: show only those without the invisible flag
            "ATTDISP" => {
                use crate::command::KeywordCommand;
                let c = KeywordCommand::new(
                    "ATTDISP",
                    "ATTDISP  attribute display  [On / Off / Normal]:",
                    vec![
                        ("On", "ON", None),
                        ("Off", "OFF", None),
                        ("Normal", "NORMAL", None),
                    ],
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("ATTDISP ") => {
                let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
                match sub.as_str() {
                    "ON" | "OFF" | "NORMAL" => {
                        self.push_undo_snapshot(i, "ATTDISP");
                        let mut count = 0usize;
                        for entity in self.tabs[i].scene.document.entities_mut() {
                            if let acadrust::EntityType::AttributeDefinition(ad) = entity {
                                match sub.as_str() {
                                    "ON" => {
                                        ad.flags.invisible = false;
                                        count += 1;
                                    }
                                    "OFF" => {
                                        ad.flags.invisible = true;
                                        count += 1;
                                    }
                                    "NORMAL" => { /* leave existing flags — they are already the "normal" state */
                                    }
                                    _ => {}
                                }
                            }
                        }
                        self.tabs[i].dirty = true;
                        self.command_line.push_output(&format!(
                            "ATTDISP {sub}: {count} attribute definition(s) updated."
                        ));
                    }
                    _ => {
                        self.command_line
                            .push_info("Usage: ATTDISP ON | OFF | NORMAL");
                    }
                }
            }

            "DONUT" => {
                use crate::modules::draw::draw::donut::DonutCommand;
                let cmd = DonutCommand::new();
                self.command_line.push_info(&cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(cmd));
            }

            "CIRCLE" => {
                use crate::modules::draw::draw::circle::CircleCommand;
                let new_cmd = CircleCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "CIRCLE_CD" => {
                use crate::modules::draw::draw::circle::CircleCDCommand;
                let new_cmd = CircleCDCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "CIRCLE_2P" => {
                use crate::modules::draw::draw::circle::Circle2PCommand;
                let new_cmd = Circle2PCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "CIRCLE_3P" => {
                use crate::modules::draw::draw::circle::Circle3PCommand;
                let new_cmd = Circle3PCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "CIRCLE_TTR" => {
                use crate::modules::draw::draw::circle::CircleTTRCommand;
                let new_cmd = CircleTTRCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.pre_cmd_tangent = Some(self.snapper.is_on(crate::snap::SnapType::Tangent));
                self.snapper.enabled.insert(crate::snap::SnapType::Tangent);
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "CIRCLE_TTT" => {
                use crate::modules::draw::draw::circle::CircleTTTCommand;
                let new_cmd = CircleTTTCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.pre_cmd_tangent = Some(self.snapper.is_on(crate::snap::SnapType::Tangent));
                self.snapper.enabled.insert(crate::snap::SnapType::Tangent);
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "ARC" => {
                use crate::modules::draw::draw::arc::ArcCommand;
                let new_cmd = ArcCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_3P" => {
                use crate::modules::draw::draw::arc::Arc3PCommand;
                let new_cmd = Arc3PCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SCE" => {
                use crate::modules::draw::draw::arc::ArcSCECommand;
                let new_cmd = ArcSCECommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SCA" => {
                use crate::modules::draw::draw::arc::ArcSCACommand;
                let new_cmd = ArcSCACommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SCL" => {
                use crate::modules::draw::draw::arc::ArcSCLCommand;
                let new_cmd = ArcSCLCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SEA" => {
                use crate::modules::draw::draw::arc::ArcSEACommand;
                let new_cmd = ArcSEACommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SER" => {
                use crate::modules::draw::draw::arc::ArcSERCommand;
                let new_cmd = ArcSERCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_SED" => {
                use crate::modules::draw::draw::arc::ArcSEDCommand;
                let new_cmd = ArcSEDCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_CSA" => {
                use crate::modules::draw::draw::arc::ArcCSACommand;
                let new_cmd = ArcCSACommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_CSL" => {
                use crate::modules::draw::draw::arc::ArcCSLCommand;
                let new_cmd = ArcCSLCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "ARC_CONT" => {
                use crate::modules::draw::draw::arc::{continue_anchor, ArcContCommand};
                // Prefer the anchor recorded when the last line/arc was drawn (it
                // knows the true drawing-end tangent); otherwise fall back to the
                // last line/arc found in the document (e.g. after a file load).
                let seed = self.cont_anchor.or_else(|| {
                    self.tabs[i]
                        .scene
                        .document
                        .entities()
                        .filter_map(|e| continue_anchor(e, None))
                        .last()
                });
                match seed {
                    Some((s, tangent)) => {
                        let new_cmd = ArcContCommand::new(s, tangent);
                        self.command_line.push_info(&new_cmd.prompt());
                        self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                    }
                    None => {
                        self.command_line
                            .push_info("ARC Continue  No previous line or arc to continue.");
                    }
                }
            }

            "RECT" | "RECTANG" => {
                use crate::modules::draw::draw::shapes::RectCommand;
                let new_cmd = RectCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                if self.ortho_mode {
                    self.rect_suppressed_ortho = true;
                    self.ortho_mode = false;
                }
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "RECT_ROT" => {
                use crate::modules::draw::draw::shapes::RectRotCommand;
                let new_cmd = RectRotCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                if self.ortho_mode {
                    self.rect_suppressed_ortho = true;
                    self.ortho_mode = false;
                }
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "RECT_CEN" => {
                use crate::modules::draw::draw::shapes::RectCenCommand;
                let new_cmd = RectCenCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                if self.ortho_mode {
                    self.rect_suppressed_ortho = true;
                    self.ortho_mode = false;
                }
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "POLY" | "POLYGON" => {
                use crate::modules::draw::draw::shapes::PolyCommand;
                let new_cmd = PolyCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "POLY_C" => {
                use crate::modules::draw::draw::shapes::PolyCCommand;
                let new_cmd = PolyCCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }
            "POLY_E" => {
                use crate::modules::draw::draw::shapes::PolyECommand;
                let new_cmd = PolyECommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "PLINE" => {
                use crate::modules::draw::draw::polyline::PlineCommand;
                let new_cmd = PlineCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "3DPOLY" => {
                use crate::modules::draw::draw::poly3d::Poly3dCommand;
                let new_cmd = Poly3dCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            // 2D filled solid. Reached via SO / SOLID2D — the bare SOLID verb is
            // currently the shaded-display toggle (token collision tracked).
            "SOLID" | "SOLID2D" => {
                use crate::modules::draw::draw::solid2d::Solid2dCommand;
                let new_cmd = Solid2dCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "HELIX" => {
                use crate::modules::draw::draw::helix::HelixCommand;
                let new_cmd = HelixCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "TRACE" => {
                use crate::modules::draw::draw::trace::TraceCommand;
                let new_cmd = TraceCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "CENTERLINE" => {
                use crate::modules::draw::draw::centerline::CenterLineCommand;
                let new_cmd = CenterLineCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "DIMCENTER" | "CENTERMARK" => {
                use crate::modules::draw::draw::dimcenter::DimCenterCommand;
                let new_cmd = DimCenterCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "SKETCH" => {
                use crate::modules::draw::draw::sketch::SketchCommand;
                let new_cmd = SketchCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "REVERSE" => {
                use crate::modules::draw::modify::reverse::ReverseCommand;
                let new_cmd = ReverseCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "MEASUREGEOM" => {
                use crate::modules::draw::inquiry::measuregeom::MeasureGeomCommand;
                let new_cmd = MeasureGeomCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            // ── Modify commands ────────────────────────────────────────────
            // MOVE works from picked points, so it already relocates entities
            // in 3D; 3DMOVE is the same operation.
            "MOVE" | "3DMOVE" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("MOVE");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::translate::MoveCommand;
                    let wires = self.tabs[i].scene.wire_models_for(&handles);
                    let new_cmd = MoveCommand::new(handles, wires);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "COPY" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("COPY");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::copy::CopyCommand;
                    let wires = self.tabs[i].scene.wire_models_for(&handles);
                    let new_cmd = CopyCommand::new(handles, wires);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "ROTATE" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("ROTATE");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::rotate::RotateCommand;
                    let wires = self.tabs[i].scene.wire_models_for(&handles);
                    let new_cmd = RotateCommand::new(handles, wires);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "TORIENT" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("TORIENT");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::torient::TorientCommand;
                    let entities: Vec<_> = handles
                        .iter()
                        .filter_map(|&h| {
                            self.tabs[i]
                                .scene
                                .document
                                .get_entity(h)
                                .cloned()
                                .map(|e| (h, e))
                        })
                        .collect();
                    let cam_rot = self.tabs[i].scene.camera.borrow().rotation;
                    let right = cam_rot * glam::Vec3::X;
                    let view_twist = right.y.atan2(right.x) as f64;
                    let new_cmd = TorientCommand::new(entities, view_twist);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "POINT" => {
                use crate::modules::draw::draw::point::PointCommand;
                let new_cmd = PointCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "RAY" => {
                use crate::modules::draw::draw::ray::RayCommand;
                let new_cmd = RayCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "XLINE" | "CONSTRUCTIONLINE" => {
                use crate::modules::draw::draw::ray::XLineCommand;
                let new_cmd = XLineCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "HATCH" => {
                use crate::modules::draw::draw::hatch::HatchCommand;
                let outlines = self.tabs[i].scene.closed_outlines();
                let new_cmd = HatchCommand::new(outlines);
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "HATCHEDIT" => {
                use crate::modules::draw::draw::hatchedit::HatcheditCommand;
                // If a single hatch is already selected, skip the pick step.
                let sel = self.tabs[i].scene.selected_entities();
                if sel.len() == 1 {
                    let (h, _) = sel[0];
                    if let Some(model) = self.tabs[i].scene.hatches.get(&h).cloned() {
                        let cmd = HatcheditCommand::with_handle(
                            h,
                            model.name.clone(),
                            model.scale,
                            model.angle_offset,
                        );
                        self.command_line.push_info(&cmd.prompt());
                        self.tabs[i].active_cmd = Some(Box::new(cmd));
                    } else {
                        self.command_line
                            .push_error("HATCHEDIT: selected entity is not a hatch.");
                    }
                } else {
                    let cmd = HatcheditCommand::new();
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                }
            }

            "GRADIENT" => {
                use crate::modules::draw::draw::hatch::GradientCommand;
                let outlines = self.tabs[i].scene.closed_outlines();
                let new_cmd = GradientCommand::new(outlines);
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "BOUNDARY" => {
                use crate::modules::draw::draw::hatch::BoundaryCommand;
                let outlines = self.tabs[i].scene.closed_outlines();
                let new_cmd = BoundaryCommand::new(outlines);
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "ELLIPSE" => {
                use crate::modules::draw::draw::ellipse::EllipseCommand;
                let new_cmd = EllipseCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "ELLIPSE_AXIS" => {
                use crate::modules::draw::draw::ellipse::EllipseAxisCommand;
                let new_cmd = EllipseAxisCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "ELLIPSE_ARC" => {
                use crate::modules::draw::draw::ellipse::EllipseArcCommand;
                let new_cmd = EllipseArcCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "SPLINE" => {
                use crate::modules::draw::draw::spline::SplineCommand;
                let new_cmd = SplineCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "SCALE" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("SCALE");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::scale::ScaleCommand;
                    let wires = self.tabs[i].scene.wire_models_for(&handles);
                    let new_cmd = ScaleCommand::new(handles, wires);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "MIRROR" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("MIRROR");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    use crate::modules::draw::modify::mirror::MirrorCommand;
                    let (wires, text_ghosts) =
                        self.tabs[i].scene.mirror_preview_parts(&handles);
                    let mirror_text = self.tabs[i].scene.document.header.mirror_text;
                    let new_cmd = MirrorCommand::new(handles, wires, text_ghosts, mirror_text);
                    self.command_line.push_info(&new_cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(new_cmd));
                }
            }

            "ERASE" => {
                let handles: Vec<_> = self.tabs[i]
                    .scene
                    .selected_entities()
                    .into_iter()
                    .map(|(h, _)| h)
                    .collect();
                if handles.is_empty() {
                    use crate::modules::draw::select::SelectObjectsCommand;
                    let cmd = SelectObjectsCommand::new("ERASE");
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                } else {
                    let n = handles.len();
                    let delta_safe = self.delta_erase_safe(i, &handles);
                    let pending = self.begin_undo(i, "ERASE", handles.len(), delta_safe);
                    // Stash the erased entities so OOPS can restore them.
                    self.oops_cache = handles
                        .iter()
                        .filter_map(|h| self.tabs[i].scene.document.get_entity_arc(*h))
                        .collect();
                    self.tabs[i].scene.erase_entities(&handles);
                    self.tabs[i].dirty = true;
                    self.refresh_properties();
                    self.command_line
                        .push_output(&format!("{n} object(s) erased."));
                    if let Some(pd) = pending {
                        self.commit_undo_delta(i, pd);
                    }
                }
            }

            // ── Model commands (3D primitives) ─────────────────────────────
            "BOX" | "WEDGE" | "CYLINDER" | "CONE" | "SPHERE" | "TORUS" => {
                use crate::modules::model::primitive_cmd::PrimitiveCommand;
                let new_cmd = PrimitiveCommand::new(cmd);
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            // ── Design commands (solid booleans) ───────────────────────────
            "UNION" | "SUBTRACT" | "INTERSECT" => {
                use crate::modules::model::boolean_cmd::BoolOp;
                if let Some(op) = BoolOp::from_id(cmd) {
                    return Some(self.solid_boolean(op));
                }
            }

            // INTERFERE — non-destructive intersect: solid from the overlap.
            "INTERFERE" => {
                return Some(self.solid_interfere());
            }

            // FLATSHOT — flatten the selected solid's edges to 2D lines at Z=0.
            "FLATSHOT" => {
                return Some(self.solid_flatshot());
            }

            // CONVTOSURFACE — convert the selected solid(s) to surface entities.
            "CONVTOSURFACE" => {
                return Some(self.solid_convtosurface());
            }

            // POLYSOLID <width> <height> — extrude a selected polyline into a
            // wall-like solid.
            "POLYSOLID" => {
                use crate::command::SelectThenTwoValueCommand;
                let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
                let c = SelectThenTwoValueCommand::new(
                    "POLYSOLID",
                    "POLYSOLID  wall width:",
                    "POLYSOLID  wall height:",
                    has_sel,
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("POLYSOLID ") => {
                let nums: Vec<f64> = cmd
                    .split_whitespace()
                    .skip(1)
                    .filter_map(|s| s.parse::<f64>().ok())
                    .collect();
                if nums.len() >= 2 && nums[0] > 0.0 && nums[1] > 0.0 {
                    return Some(self.solid_polysolid(nums[0], nums[1]));
                }
                self.command_line
                    .push_info("Usage: POLYSOLID <width> <height>   (select a polyline first)");
            }

            // SPLINEFIT — fit a smooth spline through the selected polyline's points.
            "SPLINEFIT" | "FITSPLINE" => {
                return Some(self.fit_spline());
            }

            // REGION — convert selected closed boundaries (closed polylines /
            // circles) into Region entities (one wire loop each).
            "REGION" | "REG" => {
                use acadrust::entities::{Region, Wire};
                use acadrust::types::Vector3;
                let mut loops: Vec<Vec<Vector3>> = Vec::new();
                for (_, e) in self.tabs[i].scene.selected_entities().iter() {
                    match e {
                        acadrust::EntityType::LwPolyline(pl)
                            if pl.is_closed && pl.vertices.len() >= 3 =>
                        {
                            loops.push(
                                pl.vertices
                                    .iter()
                                    .map(|v| Vector3::new(v.location.x, v.location.y, 0.0))
                                    .collect(),
                            );
                        }
                        acadrust::EntityType::Circle(c) => {
                            let n = 64;
                            loops.push(
                                (0..n)
                                    .map(|k| {
                                        let a = std::f64::consts::TAU * k as f64 / n as f64;
                                        Vector3::new(
                                            c.center.x + c.radius * a.cos(),
                                            c.center.y + c.radius * a.sin(),
                                            c.center.z,
                                        )
                                    })
                                    .collect(),
                            );
                        }
                        _ => {}
                    }
                }
                if loops.is_empty() {
                    self.command_line
                        .push_error("REGION: select closed polylines or circles.");
                } else {
                    self.push_undo_snapshot(i, "REGION");
                    let count = loops.len();
                    for pts in loops {
                        let mut w = Wire::new();
                        let first = pts.first().copied().unwrap_or(Vector3::new(0.0, 0.0, 0.0));
                        w.points = pts;
                        let mut r = Region::new();
                        r.point_of_reference = first;
                        r.wires = vec![w];
                        r.common.layer = self.tabs[i].active_layer.clone();
                        self.tabs[i].scene.add_entity(acadrust::EntityType::Region(r));
                    }
                    self.tabs[i].dirty = true;
                    self.command_line
                        .push_output(&format!("REGION: created {count} region(s)."));
                }
            }

            // PYRAMID <radius> <height> [sides] — create an n-sided pyramid mesh.
            "PYRAMID" | "PYR" => {
                use crate::command::TwoValuePromptCommand;
                let c = TwoValuePromptCommand::new(
                    "PYRAMID",
                    "PYRAMID  base radius:",
                    "PYRAMID  height (add sides by typing a 3rd number):",
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("PYRAMID ") || cmd.starts_with("PYR ") => {
                let nums: Vec<f64> = cmd
                    .split_whitespace()
                    .skip(1)
                    .filter_map(|s| s.parse::<f64>().ok())
                    .collect();
                if nums.len() >= 2 && nums[0] > 0.0 && nums[1] > 0.0 {
                    let sides = nums.get(2).map(|s| *s as usize).unwrap_or(4);
                    return Some(self.solid_pyramid(nums[0], nums[1], sides));
                }
                self.command_line
                    .push_info("Usage: PYRAMID <radius> <height> [sides]   (default 4 sides)");
            }

            // SECTION [X|Y|Z] <value> — draw the cross-section outline of the solid.
            "SECTION" => {
                use crate::command::SelectThenKeywordCommand;
                let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
                let c = SelectThenKeywordCommand::new(
                    "SECTION",
                    "SECTION  cutting-plane axis  [X / Y / Z]:",
                    vec![
                        ("X", "X", Some("SECTION  offset along X:")),
                        ("Y", "Y", Some("SECTION  offset along Y:")),
                        ("Z", "Z", Some("SECTION  offset along Z:")),
                    ],
                    has_sel,
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("SECTION ") => {
                let parts: Vec<String> = cmd
                    .split_whitespace()
                    .skip(1)
                    .map(|s| s.to_uppercase())
                    .collect();
                let (axis, val_idx) = match parts.first().map(String::as_str) {
                    Some("X") => (0, 1),
                    Some("Y") => (1, 1),
                    Some("Z") => (2, 1),
                    _ => (2, 0),
                };
                match parts.get(val_idx).and_then(|s| s.parse::<f64>().ok()) {
                    Some(v) => return Some(self.solid_section(axis, v)),
                    None => self.command_line.push_info(
                        "Usage: SECTION [X|Y|Z] <value>   (cross-sections the selected solid)",
                    ),
                }
            }

            // 3DALIGN <18 numbers> — align the selected solid by 3 source→3 dest points.
            cmd if cmd == "3DALIGN"
                || cmd == "ALIGN3D"
                || cmd.starts_with("3DALIGN ")
                || cmd.starts_with("ALIGN3D ") =>
            {
                let n: Vec<f64> = cmd
                    .split_whitespace()
                    .skip(1)
                    .filter_map(|s| s.parse::<f64>().ok())
                    .collect();
                if n.len() >= 18 {
                    let src = [[n[0], n[1], n[2]], [n[3], n[4], n[5]], [n[6], n[7], n[8]]];
                    let dst = [
                        [n[9], n[10], n[11]],
                        [n[12], n[13], n[14]],
                        [n[15], n[16], n[17]],
                    ];
                    return Some(self.solid_align3d(src, dst));
                }
                self.command_line.push_info(
                    "Usage: 3DALIGN <sx1 sy1 sz1 … sx3 sy3 sz3  dx1 dy1 dz1 … dx3 dy3 dz3>  (18 numbers: 3 source then 3 destination points)",
                );
            }

            // 3DMIRROR [X|Y|Z] — add a mirror of the selected solid across a plane.
            "3DMIRROR" | "MIRROR3D" => {
                use crate::command::SelectThenKeywordCommand;
                let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
                let c = SelectThenKeywordCommand::new(
                    "3DMIRROR",
                    "3DMIRROR  mirror plane  [X / Y / Z]:",
                    vec![("X", "X", None), ("Y", "Y", None), ("Z", "Z", None)],
                    has_sel,
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("3DMIRROR ") || cmd.starts_with("MIRROR3D ") => {
                let parts: Vec<String> = cmd
                    .split_whitespace()
                    .skip(1)
                    .map(|s| s.to_uppercase())
                    .collect();
                let axis = match parts.first().map(String::as_str) {
                    Some("X") => 0,
                    Some("Y") => 1,
                    Some("Z") => 2,
                    _ => {
                        self.command_line.push_info(
                            "Usage: 3DMIRROR [X|Y|Z]   (mirrors the selected solid across that plane)",
                        );
                        return None;
                    }
                };
                return Some(self.solid_mirror3d(axis));
            }

            // 3DROTATE [X|Y|Z] <angle> — rotate the selected solid about an axis.
            "3DROTATE" | "ROTATE3D" => {
                use crate::command::SelectThenKeywordCommand;
                let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
                let c = SelectThenKeywordCommand::new(
                    "3DROTATE",
                    "3DROTATE  rotation axis  [X / Y / Z]:",
                    vec![
                        ("X", "X", Some("3DROTATE  angle in degrees:")),
                        ("Y", "Y", Some("3DROTATE  angle in degrees:")),
                        ("Z", "Z", Some("3DROTATE  angle in degrees:")),
                    ],
                    has_sel,
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("3DROTATE ") || cmd.starts_with("ROTATE3D ") => {
                let parts: Vec<String> = cmd
                    .split_whitespace()
                    .skip(1)
                    .map(|s| s.to_uppercase())
                    .collect();
                let axis = match parts.first().map(String::as_str) {
                    Some("X") => 0,
                    Some("Y") => 1,
                    _ => 2,
                };
                let angle: Option<f64> = parts.iter().find_map(|s| s.parse::<f64>().ok());
                match angle {
                    Some(a) => return Some(self.solid_rotate3d(axis, a)),
                    None => self.command_line.push_info(
                        "Usage: 3DROTATE [X|Y|Z] <angle>   (rotates the selected solid)",
                    ),
                }
            }

            // SLICE [X|Y|Z] <value> [TOP|BOTTOM] — cut the selected solid with an
            // axis-aligned plane, keeping the lower half by default.
            "SLICE" | "SL" => {
                use crate::command::SelectThenKeywordCommand;
                let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
                let c = SelectThenKeywordCommand::new(
                    "SLICE",
                    "SLICE  cutting-plane axis  [X / Y / Z]  (add TOP/BOTTOM by typing):",
                    vec![
                        ("X", "X", Some("SLICE  offset along X:")),
                        ("Y", "Y", Some("SLICE  offset along Y:")),
                        ("Z", "Z", Some("SLICE  offset along Z:")),
                    ],
                    has_sel,
                );
                self.command_line.push_info(&c.prompt());
                self.tabs[i].active_cmd = Some(Box::new(c));
            }
            cmd if cmd.starts_with("SLICE ") || cmd.starts_with("SL ") => {
                let parts: Vec<String> = cmd
                    .split_whitespace()
                    .skip(1)
                    .map(|s| s.to_uppercase())
                    .collect();
                let (axis, val_idx) = match parts.first().map(String::as_str) {
                    Some("X") => (0, 1),
                    Some("Y") => (1, 1),
                    Some("Z") => (2, 1),
                    _ => (2, 0), // default Z plane
                };
                let value: Option<f64> = parts.get(val_idx).and_then(|s| s.parse().ok());
                let keep_low = !parts.iter().any(|s| s == "TOP");
                match value {
                    Some(v) => return Some(self.solid_slice(axis, v, keep_low)),
                    None => self.command_line.push_info(
                        "Usage: SLICE [X|Y|Z] <value> [TOP|BOTTOM]   (cuts the selected solid)",
                    ),
                }
            }

            // ── Annotate commands ──────────────────────────────────────────
            "TEXT" => {
                use crate::modules::annotate::text::TextCommand;
                let new_cmd = TextCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "DDEDIT" => {
                use crate::modules::annotate::ddedit::DdeditCommand;
                // A single text entity already selected opens its in-place
                // editor directly; otherwise prompt for a pick.
                let sel = self.tabs[i].scene.selected_entities();
                let editable = (sel.len() == 1).then(|| sel[0].0).filter(|h| {
                    self.tabs[i].scene.document.get_entity(*h).is_some_and(|e| {
                        super::super::text_inline::read_text_field(e).is_some()
                            || matches!(e, acadrust::EntityType::Leader(_))
                    })
                });
                if let Some(h) = editable {
                    return Some(self.begin_text_edit(h));
                }
                if sel.len() == 1 {
                    self.command_line
                        .push_error("DDEDIT: selected entity is not text.");
                } else {
                    let cmd = DdeditCommand::new();
                    self.command_line.push_info(&cmd.prompt());
                    self.tabs[i].active_cmd = Some(Box::new(cmd));
                }
            }

            "MTEXT" => {
                use crate::modules::annotate::mtext::MTextCommand;
                let new_cmd = MTextCommand::new();
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "TEXTEDIT" | "TEDIT" => {
                use crate::modules::annotate::textedit::TexteditCommand;
                let mode_str = if self.texteditmode {
                    "Single"
                } else {
                    "Multiple"
                };
                self.command_line
                    .push_output(&format!("Current settings: Edit mode = {}", mode_str));
                let new_cmd = TexteditCommand::new(self.texteditmode);
                self.command_line.push_info(&new_cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(new_cmd));
            }

            "TEXTEDITMODE" => {
                use crate::modules::annotate::textedit::TexteditmodeCommand;
                let cmd = TexteditmodeCommand::new(self.texteditmode);
                self.command_line.push_info(&cmd.prompt());
                self.tabs[i].active_cmd = Some(Box::new(cmd));
            }

            _ => return None,
        }
        Some(self.finish_dispatch(cmd))
    }
}
