1use 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#[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 pub fn new() -> Self {
22 Self {
23 groups: HashMap::new(),
24 node_to_group: HashMap::new(),
25 drag_state: None,
26 }
27 }
28
29 pub fn create_group(&mut self, group_id: GroupId, members: HashSet<NodeId>) -> Result<()> {
31 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 if members.is_empty() {
41 return Err(FlowError::invalid_operation("Group must have at least one member"));
42 }
43
44 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 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 pub fn get_group(&self, group_id: &GroupId) -> Option<&Group> {
67 self.groups.get(group_id)
68 }
69
70 pub fn get_group_mut(&mut self, group_id: &GroupId) -> Option<&mut Group> {
72 self.groups.get_mut(group_id)
73 }
74
75 pub fn get_node_group(&self, node_id: &NodeId) -> Option<&GroupId> {
77 self.node_to_group.get(node_id)
78 }
79
80 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 for node_id in &group.members {
89 self.node_to_group.remove(node_id);
90 }
91
92 Ok(group.members)
93 }
94
95 pub fn all_groups(&self) -> &HashMap<GroupId, Group> {
97 &self.groups
98 }
99
100 pub fn node_mappings(&self) -> &HashMap<NodeId, GroupId> {
102 &self.node_to_group
103 }
104
105 pub fn add_node_to_group(&mut self, group_id: &GroupId, node_id: NodeId) -> Result<()> {
107 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 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 group.add_member(node_id.clone());
123 self.node_to_group.insert(node_id, group_id.clone());
124
125 Ok(())
126 }
127
128 pub fn remove_node_from_group(&mut self, group_id: &GroupId, node_id: &NodeId) -> Result<()> {
130 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 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 group.remove_member(node_id);
146 self.node_to_group.remove(node_id);
147
148 if group.member_count() == 0 {
150 self.groups.remove(group_id);
151 }
152
153 Ok(())
154 }
155
156 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 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 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 pub fn group_ids(&self) -> Vec<GroupId> {
226 self.groups.keys().cloned().collect()
227 }
228
229 pub fn has_group(&self, group_id: &GroupId) -> bool {
231 self.groups.contains_key(group_id)
232 }
233
234 pub fn group_count(&self) -> usize {
236 self.groups.len()
237 }
238
239 pub fn is_node_grouped(&self, node_id: &NodeId) -> bool {
241 self.node_to_group.contains_key(node_id)
242 }
243
244 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 if self.drag_state.is_some() {
259 return Err(FlowError::invalid_operation("Group drag already in progress"));
260 }
261
262 let group = self
264 .groups
265 .get(group_id)
266 .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
267
268 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 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 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 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 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 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 pub fn is_group_dragging(&self) -> bool {
346 self.drag_state.is_some()
347 }
348
349 pub fn get_dragging_group(&self) -> Option<&GroupId> {
351 self.drag_state.as_ref().map(|state| &state.dragging_group)
352 }
353
354 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 pub fn has_active_drag(&self) -> bool {
363 self.drag_state.is_some()
364 }
365
366 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 let group = self.get_group(group_id)
373 .ok_or_else(|| FlowError::invalid_operation(format!("Group {} not found", group_id)))?;
374
375 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}