pub struct App<const COLS: usize, const ROWS: usize> {
    state_receiver: tokio::sync::watch::Receiver<osc::State<COLS, ROWS>>,
    interpolation: crate::interpolation::Interpolation,
    pendulum: crate::pendulum::Pendulum,
}

impl<const COLS: usize, const ROWS: usize> App<COLS, ROWS> {
    pub fn new(state_receiver: tokio::sync::watch::Receiver<osc::State<COLS, ROWS>>) -> Self {
        let interpolation = crate::interpolation::Interpolation::new(std::time::Instant::now());
        let pendulum = crate::pendulum::Pendulum::new();
        Self {
            state_receiver,
            interpolation,
            pendulum,
        }
    }
}

impl<const COLS: usize, const ROWS: usize> eframe::App for App<COLS, ROWS> {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        let state = self.state_receiver.borrow_and_update().clone();
        let interpolated = self.interpolation.update(&state, std::time::Instant::now());
        let pendulum_position = self.pendulum.update(&interpolated);

        ctx.style_mut(|style| {
            style.visuals.panel_fill = egui::Color32::from_rgb(17, 17, 17);
            style.visuals.window_fill = egui::Color32::from_rgb(17, 17, 17);
            style.spacing.item_spacing = egui::vec2(0.0, 0.0);
        });

        egui::CentralPanel::default().show(ctx, |ui| {
            ui.vertical(|ui| {
                ui.add(super::Metronome::new(pendulum_position));
                ui.add(super::Matrix::<COLS, ROWS>::new(
                    &state.columns,
                    state.selected_column,
                    state.selected_row,
                    interpolated.position_in_beat, // Pass metronome position to Matrix
                ));
            });
        });

        ctx.request_repaint();
    }
}
