flow_rs_core/groups/
manager.rs

1//! Group manager for handling group operations
2
3use crate::error::{FlowError, Result};
4use crate::graph::Graph;
5use crate::types::{GroupId, NodeId, Position};
6use std::collections::{HashMap, HashSet};
7
8use super::drag_state::GroupDragState;
9use super::group::Group;
10
11/// Group manager for handling group operations
12#[derive(Debug, Clone)]
13pub struct GroupManager {
14    groups: HashMap<GroupId, Group>,
15    node_to_group: HashMap<NodeId, GroupId>,
16    drag_state: Option<GroupDragState>,
17}
18
19impl GroupManager {
20    /// Create a new group manager
21    pub fn new() -> Self {
22        Self {
23            groups: HashMap::new(),
24            node_to_group: HashMap::new(),
25            drag_state: None,
26        }
27    }
28
29    /// Create a new group with the given members
30    pub fn create_group(&mut self, group_id: GroupId, members: HashSet<NodeId>) -> Result<()> {
31        // Validate that group doesn't already exist
32        if self.groups.contains_key(&group_id) {
33            return Err(FlowError::invalid_operation(format!(
34                "Group {} already exists",
35                group_id
36            )));
37        }
38
39        // Validate that group has members
40        if members.is_empty() {
41            return Err(FlowError::invalid_operation("Group must have at least one member"));
42        }
43
44        // Validate that all nodes are not already in groups
45        for node_id in &members {
46            if self.node_to_group.contains_key(node_id) {
47                return Err(FlowError::invalid_operation(format!(
48                    "Node {} is already in a group",
49                    node_id
50                )));
51            }
52        }
53
54        // Create group and update mappings
55        let group = Group::new(group_id.clone(), members.clone());
56        self.groups.insert(group_id.clone(), group);
57
58        for node_id in members {
59            self.node_to_group.insert(node_id, group_id.clone());
60        }
61
62        Ok(())
63    }
64
65    /// Get a group by ID
66    pub fn get_group(&self, group_id: &GroupId) -> Option<&Group> {
67        self.groups.get(group_id)
68    }
69
70    /// Get a mutable reference to a group by ID
71    pub fn get_group_mut(&mut self, group_id: &GroupId) -> Option<&mut Group> {
72        self.groups.get_mut(group_id)
73    }
74
75    /// Get the group ID for a node
76    pub fn get_node_group(&self, node_id: &NodeId) -> Option<&GroupId> {
77        self.node_to_group.get(node_id)
78    }
79
80    /// Dissolve a group and return its members
81    pub fn dissolve_group(&mut self, group_id: &GroupId) -> Result<HashSet<NodeId>> {
82        let group = self
83            .groups
84            .remove(group_id)
85            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
86
87        // Remove node-to-group mappings
88        for node_id in &group.members {
89            self.node_to_group.remove(node_id);
90        }
91
92        Ok(group.members)
93    }
94
95    /// Get all groups
96    pub fn all_groups(&self) -> &HashMap<GroupId, Group> {
97        &self.groups
98    }
99
100    /// Get all node-to-group mappings
101    pub fn node_mappings(&self) -> &HashMap<NodeId, GroupId> {
102        &self.node_to_group
103    }
104
105    /// Add a node to an existing group
106    pub fn add_node_to_group(&mut self, group_id: &GroupId, node_id: NodeId) -> Result<()> {
107        // Check if node is already in a group
108        if self.node_to_group.contains_key(&node_id) {
109            return Err(FlowError::invalid_operation(format!(
110                "Node {} is already in a group",
111                node_id
112            )));
113        }
114
115        // Check if group exists
116        let group = self
117            .groups
118            .get_mut(group_id)
119            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
120
121        // Add node to group and update mapping
122        group.add_member(node_id.clone());
123        self.node_to_group.insert(node_id, group_id.clone());
124
125        Ok(())
126    }
127
128    /// Remove a node from a group
129    pub fn remove_node_from_group(&mut self, group_id: &GroupId, node_id: &NodeId) -> Result<()> {
130        // Check if group exists
131        let group = self
132            .groups
133            .get_mut(group_id)
134            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
135
136        // Check if node is in this group
137        if !group.contains_node(node_id) {
138            return Err(FlowError::invalid_operation(format!(
139                "Node {} is not in group {}",
140                node_id, group_id
141            )));
142        }
143
144        // Remove node from group and clear mapping
145        group.remove_member(node_id);
146        self.node_to_group.remove(node_id);
147
148        // If group is now empty, dissolve it
149        if group.member_count() == 0 {
150            self.groups.remove(group_id);
151        }
152
153        Ok(())
154    }
155
156    /// Set the name of a group
157    pub fn set_group_name(&mut self, group_id: &GroupId, name: Option<String>) -> Result<()> {
158        let group = self
159            .groups
160            .get_mut(group_id)
161            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
162
163        group.name = name;
164        Ok(())
165    }
166
167    /// Calculate the bounding rectangle for a group based on its member positions
168    pub fn calculate_group_bounds<N, E>(
169        &mut self,
170        group_id: &GroupId,
171        graph: &Graph<N, E>,
172    ) -> Result<()>
173    where
174        N: Clone,
175        E: Clone,
176    {
177        let group = self
178            .groups
179            .get_mut(group_id)
180            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
181
182        if group.members.is_empty() {
183            return Ok(());
184        }
185
186        let mut min_x = f64::INFINITY;
187        let mut min_y = f64::INFINITY;
188        let mut max_x = f64::NEG_INFINITY;
189        let mut max_y = f64::NEG_INFINITY;
190
191        for node_id in &group.members {
192            if let Some(node) = graph.get_node(node_id) {
193                let node_left = node.position.x;
194                let node_right = node.position.x + node.size.width;
195                let node_top = node.position.y;
196                let node_bottom = node.position.y + node.size.height;
197
198                min_x = min_x.min(node_left);
199                min_y = min_y.min(node_top);
200                max_x = max_x.max(node_right);
201                max_y = max_y.max(node_bottom);
202            }
203        }
204
205        group.position = Position::new(min_x, min_y);
206        group.size = crate::types::Size::new(max_x - min_x, max_y - min_y);
207
208        Ok(())
209    }
210
211    /// Update group bounds based on current member positions
212    pub fn update_group_bounds<N, E>(
213        &mut self,
214        group_id: &GroupId,
215        graph: &Graph<N, E>,
216    ) -> Result<()>
217    where
218        N: Clone,
219        E: Clone,
220    {
221        self.calculate_group_bounds(group_id, graph)
222    }
223
224    /// Get all group IDs
225    pub fn group_ids(&self) -> Vec<GroupId> {
226        self.groups.keys().cloned().collect()
227    }
228
229    /// Check if a group exists
230    pub fn has_group(&self, group_id: &GroupId) -> bool {
231        self.groups.contains_key(group_id)
232    }
233
234    /// Get the number of groups
235    pub fn group_count(&self) -> usize {
236        self.groups.len()
237    }
238
239    /// Check if a node is in any group
240    pub fn is_node_grouped(&self, node_id: &NodeId) -> bool {
241        self.node_to_group.contains_key(node_id)
242    }
243
244    // Group Drag Operations
245
246    /// Start a group drag operation
247    pub fn start_group_drag<N, E>(
248        &mut self,
249        group_id: &GroupId,
250        start_position: Position,
251        graph: &Graph<N, E>,
252    ) -> Result<()>
253    where
254        N: Clone,
255        E: Clone,
256    {
257        // Check if already dragging
258        if self.drag_state.is_some() {
259            return Err(FlowError::invalid_operation("Group drag already in progress"));
260        }
261
262        // Check if group exists
263        let group = self
264            .groups
265            .get(group_id)
266            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
267
268        // Create drag state
269        let mut drag_state = GroupDragState::new(group_id.clone(), start_position);
270        drag_state.store_original_positions(group, graph)?;
271
272        self.drag_state = Some(drag_state);
273        Ok(())
274    }
275
276    /// Update group drag position
277    pub fn update_group_drag<N, E>(
278        &mut self,
279        new_position: Position,
280        graph: &mut Graph<N, E>,
281    ) -> Result<Position>
282    where
283        N: Clone,
284        E: Clone,
285    {
286        let drag_state = self
287            .drag_state
288            .as_mut()
289            .ok_or_else(|| FlowError::invalid_operation("No group drag in progress"))?;
290
291        let delta = drag_state.update_position(new_position);
292
293        // Move all nodes in the group
294        if let Some(group) = self.groups.get(&drag_state.dragging_group) {
295            for node_id in &group.members {
296                if let Some(node) = graph.get_node_mut(node_id) {
297                    if let Some(original_pos) = drag_state.original_node_positions.get(node_id) {
298                        node.position = Position::new(
299                            original_pos.x + delta.x,
300                            original_pos.y + delta.y,
301                        );
302                    }
303                }
304            }
305        }
306
307        Ok(delta)
308    }
309
310    /// Complete group drag operation
311    pub fn complete_group_drag(&mut self) -> Result<GroupId> {
312        let drag_state = self
313            .drag_state
314            .take()
315            .ok_or_else(|| FlowError::invalid_operation("No group drag in progress"))?;
316
317        Ok(drag_state.dragging_group)
318    }
319
320    /// Cancel group drag operation and restore original positions
321    pub fn cancel_group_drag<N, E>(
322        &mut self,
323        graph: &mut Graph<N, E>,
324    ) -> Result<GroupId>
325    where
326        N: Clone,
327        E: Clone,
328    {
329        let drag_state = self
330            .drag_state
331            .take()
332            .ok_or_else(|| FlowError::invalid_operation("No group drag in progress"))?;
333
334        // Restore original positions
335        for (node_id, original_position) in &drag_state.original_node_positions {
336            if let Some(node) = graph.get_node_mut(node_id) {
337                node.position = *original_position;
338            }
339        }
340
341        Ok(drag_state.dragging_group)
342    }
343
344    /// Check if a group is currently being dragged
345    pub fn is_group_dragging(&self) -> bool {
346        self.drag_state.is_some()
347    }
348
349    /// Get the current dragging group ID (if any)
350    pub fn get_dragging_group(&self) -> Option<&GroupId> {
351        self.drag_state.as_ref().map(|state| &state.dragging_group)
352    }
353
354    /// Get the current drag delta (if any)
355    pub fn get_drag_delta(&self) -> Option<Position> {
356        self.drag_state
357            .as_ref()
358            .map(|state| state.delta_from_start())
359    }
360
361    /// Check if there's an active drag operation
362    pub fn has_active_drag(&self) -> bool {
363        self.drag_state.is_some()
364    }
365
366    /// Move a group by the given delta
367    pub fn move_group<N, E>(&mut self, group_id: &GroupId, delta: Position, graph: &mut Graph<N, E>) -> Result<()> 
368    where
369        N: Clone,
370    {
371        // Get the group
372        let group = self.get_group(group_id)
373            .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
374
375        // Move all nodes in the group
376        for node_id in &group.members {
377            if let Some(node) = graph.get_node_mut(node_id) {
378                let new_position = Position::new(
379                    node.position.x + delta.x,
380                    node.position.y + delta.y,
381                );
382                node.set_position(new_position);
383            }
384        }
385
386        Ok(())
387    }
388}