1use crate::core;
3use crate::core::mouse;
4use crate::core::renderer;
5use crate::core::widget;
6use crate::core::{Element, Point, Size};
7use crate::instruction;
8use crate::program;
9use crate::program::Program;
10use crate::runtime;
11use crate::runtime::futures::futures::StreamExt;
12use crate::runtime::futures::futures::channel::mpsc;
13use crate::runtime::futures::futures::stream;
14use crate::runtime::futures::subscription;
15use crate::runtime::futures::{Executor, Runtime};
16use crate::runtime::task;
17use crate::runtime::user_interface;
18use crate::runtime::window;
19use crate::runtime::{Task, UserInterface};
20use crate::{Instruction, Selector};
21
22use std::fmt;
23
24pub struct Emulator<P: Program> {
34    state: P::State,
35    runtime: Runtime<P::Executor, mpsc::Sender<Event<P>>, Event<P>>,
36    renderer: P::Renderer,
37    mode: Mode,
38    size: Size,
39    window: core::window::Id,
40    cursor: mouse::Cursor,
41    clipboard: Clipboard,
42    cache: Option<user_interface::Cache>,
43    pending_tasks: usize,
44}
45
46pub enum Event<P: Program> {
48    Action(Action<P>),
50    Failed(Instruction),
52    Ready,
54}
55
56pub struct Action<P: Program>(Action_<P>);
58
59enum Action_<P: Program> {
60    Runtime(runtime::Action<P::Message>),
61    CountDown,
62}
63
64impl<P: Program + 'static> Emulator<P> {
65    pub fn new(
71        sender: mpsc::Sender<Event<P>>,
72        program: &P,
73        mode: Mode,
74        size: Size,
75    ) -> Emulator<P> {
76        Self::with_preset(sender, program, mode, size, None)
77    }
78
79    pub fn with_preset(
84        sender: mpsc::Sender<Event<P>>,
85        program: &P,
86        mode: Mode,
87        size: Size,
88        preset: Option<&program::Preset<P::State, P::Message>>,
89    ) -> Emulator<P> {
90        use renderer::Headless;
91
92        let settings = program.settings();
93
94        let executor = P::Executor::new().expect("Create emulator executor");
96
97        let renderer = executor
98            .block_on(P::Renderer::new(
99                settings.default_font,
100                settings.default_text_size,
101                None,
102            ))
103            .expect("Create emulator renderer");
104
105        let runtime = Runtime::new(executor, sender);
106
107        let (state, task) = runtime.enter(|| {
108            if let Some(preset) = preset {
109                preset.boot()
110            } else {
111                program.boot()
112            }
113        });
114
115        let mut emulator = Self {
116            state,
117            runtime,
118            renderer,
119            mode,
120            size,
121            clipboard: Clipboard { content: None },
122            cursor: mouse::Cursor::Unavailable,
123            window: core::window::Id::unique(),
124            cache: Some(user_interface::Cache::default()),
125            pending_tasks: 0,
126        };
127
128        emulator.resubscribe(program);
129        emulator.wait_for(task);
130
131        emulator
132    }
133
134    pub fn update(&mut self, program: &P, message: P::Message) {
140        let task = self
141            .runtime
142            .enter(|| program.update(&mut self.state, message));
143
144        self.resubscribe(program);
145
146        match self.mode {
147            Mode::Zen if self.pending_tasks > 0 => self.wait_for(task),
148            _ => {
149                if let Some(stream) = task::into_stream(task) {
150                    self.runtime.run(
151                        stream
152                            .map(Action_::Runtime)
153                            .map(Action)
154                            .map(Event::Action)
155                            .boxed(),
156                    );
157                }
158            }
159        }
160    }
161
162    pub fn perform(&mut self, program: &P, action: Action<P>) {
167        match action.0 {
168            Action_::CountDown => {
169                if self.pending_tasks > 0 {
170                    self.pending_tasks -= 1;
171
172                    if self.pending_tasks == 0 {
173                        self.runtime.send(Event::Ready);
174                    }
175                }
176            }
177            Action_::Runtime(action) => match action {
178                runtime::Action::Output(message) => {
179                    self.update(program, message);
180                }
181                runtime::Action::LoadFont { .. } => {
182                    }
184                runtime::Action::Widget(operation) => {
185                    let mut user_interface = UserInterface::build(
186                        program.view(&self.state, self.window),
187                        self.size,
188                        self.cache.take().unwrap(),
189                        &mut self.renderer,
190                    );
191
192                    let mut operation = Some(operation);
193
194                    while let Some(mut current) = operation.take() {
195                        user_interface.operate(&self.renderer, &mut current);
196
197                        match current.finish() {
198                            widget::operation::Outcome::None => {}
199                            widget::operation::Outcome::Some(()) => {}
200                            widget::operation::Outcome::Chain(next) => {
201                                operation = Some(next);
202                            }
203                        }
204                    }
205
206                    self.cache = Some(user_interface.into_cache());
207                }
208                runtime::Action::Clipboard(action) => {
209                    dbg!(action);
211                }
212                runtime::Action::Window(action) => match action {
213                    window::Action::Open(id, _settings, sender) => {
214                        self.window = id;
215
216                        let _ = sender.send(self.window);
217                    }
218                    window::Action::GetOldest(sender)
219                    | window::Action::GetLatest(sender) => {
220                        let _ = sender.send(Some(self.window));
221                    }
222                    window::Action::GetSize(id, sender) => {
223                        if id == self.window {
224                            let _ = sender.send(self.size);
225                        }
226                    }
227                    window::Action::GetMaximized(id, sender) => {
228                        if id == self.window {
229                            let _ = sender.send(false);
230                        }
231                    }
232                    window::Action::GetMinimized(id, sender) => {
233                        if id == self.window {
234                            let _ = sender.send(None);
235                        }
236                    }
237                    window::Action::GetPosition(id, sender) => {
238                        if id == self.window {
239                            let _ = sender.send(Some(Point::ORIGIN));
240                        }
241                    }
242                    window::Action::GetScaleFactor(id, sender) => {
243                        if id == self.window {
244                            let _ = sender.send(1.0);
245                        }
246                    }
247                    window::Action::GetMode(id, sender) => {
248                        if id == self.window {
249                            let _ = sender.send(core::window::Mode::Windowed);
250                        }
251                    }
252                    _ => {
253                        }
255                },
256                runtime::Action::System(action) => {
257                    dbg!(action);
259                }
260                iced_runtime::Action::Image(action) => {
261                    dbg!(action);
263                }
264                runtime::Action::Exit => {
265                    }
267                runtime::Action::Reload => {
268                    }
270            },
271        }
272    }
273
274    pub fn run(&mut self, program: &P, instruction: Instruction) {
281        let mut user_interface = UserInterface::build(
282            program.view(&self.state, self.window),
283            self.size,
284            self.cache.take().unwrap(),
285            &mut self.renderer,
286        );
287
288        let mut messages = Vec::new();
289
290        match &instruction {
291            Instruction::Interact(interaction) => {
292                let Some(events) = interaction.events(|target| match target {
293                    instruction::Target::Point(position) => Some(*position),
294                    instruction::Target::Text(text) => {
295                        use widget::Operation;
296
297                        let mut operation = Selector::find(text.as_str());
298
299                        user_interface.operate(
300                            &self.renderer,
301                            &mut widget::operation::black_box(&mut operation),
302                        );
303
304                        match operation.finish() {
305                            widget::operation::Outcome::Some(text) => {
306                                Some(text?.visible_bounds()?.center())
307                            }
308                            _ => None,
309                        }
310                    }
311                }) else {
312                    self.runtime.send(Event::Failed(instruction));
313                    self.cache = Some(user_interface.into_cache());
314                    return;
315                };
316
317                for event in &events {
318                    if let core::Event::Mouse(mouse::Event::CursorMoved {
319                        position,
320                    }) = event
321                    {
322                        self.cursor = mouse::Cursor::Available(*position);
323                    }
324                }
325
326                let (_state, _status) = user_interface.update(
327                    &events,
328                    self.cursor,
329                    &mut self.renderer,
330                    &mut self.clipboard,
331                    &mut messages,
332                );
333
334                self.cache = Some(user_interface.into_cache());
335
336                let task = self.runtime.enter(|| {
337                    Task::batch(messages.into_iter().map(|message| {
338                        program.update(&mut self.state, message)
339                    }))
340                });
341
342                self.resubscribe(program);
343                self.wait_for(task);
344            }
345            Instruction::Expect(expectation) => match expectation {
346                instruction::Expectation::Text(text) => {
347                    use widget::Operation;
348
349                    let mut operation = Selector::find(text.as_str());
350
351                    user_interface.operate(
352                        &self.renderer,
353                        &mut widget::operation::black_box(&mut operation),
354                    );
355
356                    match operation.finish() {
357                        widget::operation::Outcome::Some(Some(_text)) => {
358                            self.runtime.send(Event::Ready);
359                        }
360                        _ => {
361                            self.runtime.send(Event::Failed(instruction));
362                        }
363                    }
364
365                    self.cache = Some(user_interface.into_cache());
366                }
367            },
368        }
369    }
370
371    fn wait_for(&mut self, task: Task<P::Message>) {
372        if let Some(stream) = task::into_stream(task) {
373            match self.mode {
374                Mode::Zen => {
375                    self.pending_tasks += 1;
376
377                    self.runtime.run(
378                        stream
379                            .map(Action_::Runtime)
380                            .map(Action)
381                            .map(Event::Action)
382                            .chain(stream::once(async {
383                                Event::Action(Action(Action_::CountDown))
384                            }))
385                            .boxed(),
386                    );
387                }
388                Mode::Patient => {
389                    self.runtime.run(
390                        stream
391                            .map(Action_::Runtime)
392                            .map(Action)
393                            .map(Event::Action)
394                            .chain(stream::once(async { Event::Ready }))
395                            .boxed(),
396                    );
397                }
398                Mode::Immediate => {
399                    self.runtime.run(
400                        stream
401                            .map(Action_::Runtime)
402                            .map(Action)
403                            .map(Event::Action)
404                            .boxed(),
405                    );
406                    self.runtime.send(Event::Ready);
407                }
408            }
409        } else if self.pending_tasks == 0 {
410            self.runtime.send(Event::Ready);
411        }
412    }
413
414    fn resubscribe(&mut self, program: &P) {
415        self.runtime
416            .track(subscription::into_recipes(self.runtime.enter(|| {
417                program.subscription(&self.state).map(|message| {
418                    Event::Action(Action(Action_::Runtime(
419                        runtime::Action::Output(message),
420                    )))
421                })
422            })));
423    }
424
425    pub fn view(
427        &self,
428        program: &P,
429    ) -> Element<'_, P::Message, P::Theme, P::Renderer> {
430        program.view(&self.state, self.window)
431    }
432
433    pub fn theme(&self, program: &P) -> Option<P::Theme> {
435        program.theme(&self.state, self.window)
436    }
437
438    pub fn into_state(self) -> (P::State, core::window::Id) {
440        (self.state, self.window)
441    }
442}
443
444#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
449pub enum Mode {
450    #[default]
455    Zen,
456    Patient,
458    Immediate,
460}
461
462impl Mode {
463    pub const ALL: &[Self] = &[Self::Zen, Self::Patient, Self::Immediate];
465}
466
467impl fmt::Display for Mode {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        f.write_str(match self {
470            Self::Zen => "Zen",
471            Self::Patient => "Patient",
472            Self::Immediate => "Immediate",
473        })
474    }
475}
476
477struct Clipboard {
478    content: Option<String>,
479}
480
481impl core::Clipboard for Clipboard {
482    fn read(&self, _kind: core::clipboard::Kind) -> Option<String> {
483        self.content.clone()
484    }
485
486    fn write(&mut self, _kind: core::clipboard::Kind, contents: String) {
487        self.content = Some(contents);
488    }
489}