1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use std::cell::RefCell;

use piet::RenderContext;
use std::rc::Rc;

use crate::runtime::StackFrame;
use crate::PaxEngine;

use pax_runtime_api::{EasingCurve, PropertyInstance, TransitionManager, TransitionQueueEntry};

// The `Expression` form of a property — stores a function
// that evaluates the value itself, as well as a "register" of
// the memoized value (`cached_value`) that can be referred to
// via calls to `read()`
pub struct PropertyExpression<T: Default> {
    pub id: usize,
    pub cached_value: T,
    pub transition_manager: TransitionManager<T>,
}

impl<T: Default> PropertyExpression<T> {
    pub fn new(id: usize) -> Self {
        Self {
            id,
            cached_value: Default::default(),
            transition_manager: TransitionManager::new(),
        }
    }
}

impl<T: Default + Clone> PropertyInstance<T> for PropertyExpression<T> {
    fn get(&self) -> &T {
        &self.cached_value
    }

    fn get_mut(&mut self) -> &mut T {
        unreachable!()
    }

    // fn is_fresh(&self) -> bool {
    //     self.is_fresh
    // }
    //
    // fn _mark_not_fresh(&mut self) {
    //     self.is_fresh = false;
    // }

    fn _get_vtable_id(&self) -> Option<usize> {
        Some(self.id)
    }

    fn set(&mut self, value: T) {
        self.cached_value = value;
    }

    //FUTURE: when trait fields land, DRY this implementation vs. other <T: PropertyInstance> implementations
    fn ease_to(&mut self, new_value: T, duration_frames: u64, curve: EasingCurve) {
        self.transition_manager.value = Some(self.get().clone());
        self.transition_manager.queue.clear();
        self.transition_manager
            .queue
            .push_back(TransitionQueueEntry {
                global_frame_started: None,
                duration_frames,
                curve,
                starting_value: self.cached_value.clone(),
                ending_value: new_value,
            });
    }

    fn ease_to_later(&mut self, new_value: T, duration_frames: u64, curve: EasingCurve) {
        if let None = self.transition_manager.value {
            //handle case where transition queue is empty -- a None value gets skipped, so populate it with Some
            self.transition_manager.value = Some(self.get().clone());
        }
        self.transition_manager
            .queue
            .push_back(TransitionQueueEntry {
                global_frame_started: None,
                duration_frames,
                curve,
                starting_value: self.cached_value.clone(),
                ending_value: new_value,
            });
    }

    fn _get_transition_manager(&mut self) -> Option<&mut TransitionManager<T>> {
        if let None = self.transition_manager.value {
            None
        } else {
            Some(&mut self.transition_manager)
        }
    }
}

/// Data structure used for dynamic injection of values
/// into Expressions, maintaining a pointer e.g. to the current
/// stack frame to enable evaluation of properties & dependencies
pub struct ExpressionContext<'a, R: 'static + RenderContext> {
    pub engine: &'a PaxEngine<R>,
    pub stack_frame: Rc<RefCell<StackFrame<R>>>,
    //TODO: is the following the right approach to enabling evaluation of built-ins?
    // pub render_node: RenderNodePtr<R>,
}