1use std::time::Instant;
4
5use a7n_proto::limits;
6use a7n_proto::v1::{
7 Accept, Focus as WireFocus, Handover, InputEvent, Landing, Refuse, RefuseReason, envelope::Body,
8};
9use a7n_proto::{MachineId, Route};
10
11use super::{Action, Driven, Focus};
12
13impl Focus {
14 pub fn on_focus(
16 &mut self,
17 peer: MachineId,
18 focus: &WireFocus,
19 allow_drive: bool,
20 now: Instant,
21 ) -> Vec<Action> {
22 if self
23 .accepted
24 .get(&peer)
25 .is_some_and(|route| focus.route <= route.0)
26 {
27 return Vec::new();
28 }
29 let Ok(target) = MachineId::try_from(focus.target.as_slice()) else {
30 return vec![Self::refuse(
31 peer,
32 focus.route,
33 RefuseReason::Malformed,
34 None,
35 )];
36 };
37 let Ok(reach) = focus
38 .reach
39 .iter()
40 .map(|id| MachineId::try_from(id.as_slice()))
41 .collect::<Result<Vec<_>, _>>()
42 else {
43 return vec![Self::refuse(
44 peer,
45 focus.route,
46 RefuseReason::Malformed,
47 None,
48 )];
49 };
50 if target != self.own_id {
51 self.accepted.insert(peer, Route(focus.route));
53 let mut actions = Vec::new();
54 if self
55 .driven
56 .as_ref()
57 .is_some_and(|driven| driven.owner == peer)
58 {
59 self.end_driven(&mut actions);
60 }
61 return actions;
62 }
63 if !allow_drive {
64 return vec![Self::refuse(
65 peer,
66 focus.route,
67 RefuseReason::NotAllowed,
68 None,
69 )];
70 }
71 self.accept_take(peer, focus, reach, now)
72 }
73
74 fn accept_take(
75 &mut self,
76 peer: MachineId,
77 focus: &WireFocus,
78 reach: Vec<MachineId>,
79 now: Instant,
80 ) -> Vec<Action> {
81 let mut actions = Vec::new();
82 if let Some(driven) = &self.driven {
83 if driven.owner != peer {
84 if now.saturating_duration_since(driven.last_input) < limits::IDLE_TAKEOVER {
85 return vec![Self::refuse(
86 peer,
87 focus.route,
88 RefuseReason::Owned,
89 Some(driven.owner),
90 )];
91 }
92 self.take_over(peer, &mut actions);
93 }
94 } else if self
95 .last_local_input
96 .is_some_and(|last| now.saturating_duration_since(last) < limits::IDLE_TAKEOVER)
97 {
98 return vec![Self::refuse(
99 peer,
100 focus.route,
101 RefuseReason::Busy,
102 Some(self.own_id),
103 )];
104 }
105 actions.extend(self.go_home());
106 self.accepted.insert(peer, Route(focus.route));
107 self.driven = Some(Driven {
108 owner: peer,
109 route: Route(focus.route),
110 last_input: now,
111 reach,
112 });
113 actions.push(Self::send(
114 peer,
115 Body::Accept(Accept { route: focus.route }),
116 ));
117 actions.push(Action::Driven {
118 owner: peer,
119 landing: if focus.stay {
120 None
121 } else {
122 focus.landing.clone()
123 },
124 resistance_px: focus.resistance_px,
125 });
126 actions
127 }
128
129 pub fn on_remote_input(&mut self, peer: MachineId, route: Route, now: Instant) -> bool {
132 let Some(driven) = self.driven.as_mut() else {
133 return false;
134 };
135 if driven.owner != peer || driven.route != route {
136 return false;
137 }
138 driven.last_input = now;
139 true
140 }
141
142 pub fn on_input_event(
145 &mut self,
146 peer: MachineId,
147 route: Route,
148 event: &InputEvent,
149 now: Instant,
150 ) -> bool {
151 if !self.on_remote_input(peer, route, now) {
152 return false;
153 }
154 self.held.apply(event)
155 }
156
157 pub fn on_local_input(&mut self, now: Instant) -> (bool, Vec<Action>) {
160 let mut actions = Vec::new();
161 if let Some(driven) = &self.driven {
162 if now.saturating_duration_since(driven.last_input) < limits::IDLE_TAKEOVER {
163 return (false, actions);
164 }
165 self.take_over(self.own_id, &mut actions);
166 }
167 self.last_local_input = Some(now);
168 (true, actions)
169 }
170
171 #[must_use]
173 pub fn handover(&self, next: MachineId, landing: Option<Landing>) -> Vec<Action> {
174 let Some(driven) = &self.driven else {
175 return Vec::new();
176 };
177 if next != driven.owner && !driven.reach.contains(&next) {
178 return Vec::new();
179 }
180 vec![Self::send(
181 driven.owner,
182 Body::Handover(Handover {
183 route: driven.route.0,
184 next: next.as_bytes().to_vec(),
185 landing,
186 taken_over_by: Vec::new(),
187 }),
188 )]
189 }
190
191 pub fn deny_drive(&mut self, peer: MachineId) -> Vec<Action> {
193 let mut actions = Vec::new();
194 if self
195 .driven
196 .as_ref()
197 .is_some_and(|driven| driven.owner == peer)
198 {
199 self.end_driven(&mut actions);
200 }
201 actions
202 }
203
204 pub(super) fn end_driven(&mut self, actions: &mut Vec<Action>) {
205 self.driven = None;
206 let released = self.held.release_all();
207 if !released.is_empty() {
208 actions.push(Action::Release(released));
209 }
210 actions.push(Action::EndedDriven);
211 }
212
213 fn take_over(&mut self, replacement: MachineId, actions: &mut Vec<Action>) {
214 let Some(driven) = self.driven.take() else {
215 return;
216 };
217 self.end_driven(actions);
218 actions.push(Self::send(
219 driven.owner,
220 Body::Handover(Handover {
221 route: driven.route.0,
222 next: driven.owner.as_bytes().to_vec(),
223 landing: None,
224 taken_over_by: replacement.as_bytes().to_vec(),
225 }),
226 ));
227 }
228
229 fn refuse(
230 peer: MachineId,
231 route: u64,
232 reason: RefuseReason,
233 other: Option<MachineId>,
234 ) -> Action {
235 Self::send(
236 peer,
237 Body::Refuse(Refuse {
238 route,
239 reason: reason.into(),
240 other: other.map_or_else(Vec::new, |id| id.as_bytes().to_vec()),
241 }),
242 )
243 }
244}