use std::sync::atomic::{AtomicU64, Ordering};

use rusty_link::{AblLink, SessionState};

pub struct LinkState {
    link: AblLink,
    quantum: AtomicU64,
}

impl LinkState {
    pub fn new(tempo: f64, quantum: f64) -> Self {
        let link = AblLink::new(tempo);
        Self {
            link,
            quantum: AtomicU64::new(quantum.to_bits()),
        }
    }

    pub fn is_enabled(&self) -> bool {
        self.link.is_enabled()
    }

    pub fn set_enabled(&self, enabled: bool) {
        self.link.enable(enabled);
    }

    pub fn enable(&self) {
        self.link.enable(true);
    }

    pub fn is_start_stop_sync_enabled(&self) -> bool {
        self.link.is_start_stop_sync_enabled()
    }

    pub fn set_start_stop_sync_enabled(&self, enabled: bool) {
        self.link.enable_start_stop_sync(enabled);
    }

    pub fn quantum(&self) -> f64 {
        f64::from_bits(self.quantum.load(Ordering::Acquire))
    }

    pub fn set_quantum(&self, quantum: f64) {
        let clamped = quantum.clamp(1.0, 16.0);
        self.quantum.store(clamped.to_bits(), Ordering::Release);
    }

    pub fn clock_micros(&self) -> i64 {
        self.link.clock_micros()
    }

    pub fn tempo(&self) -> f64 {
        let mut state = SessionState::new();
        self.link.capture_app_session_state(&mut state);
        state.tempo()
    }

    pub fn beat(&self) -> f64 {
        let mut state = SessionState::new();
        self.link.capture_app_session_state(&mut state);
        let time = self.link.clock_micros();
        state.beat_at_time(time, self.quantum())
    }

    pub fn phase(&self) -> f64 {
        let mut state = SessionState::new();
        self.link.capture_app_session_state(&mut state);
        let time = self.link.clock_micros();
        state.phase_at_time(time, self.quantum())
    }

    pub fn peers(&self) -> u64 {
        self.link.num_peers()
    }

    pub fn set_tempo(&self, tempo: f64) {
        let mut state = SessionState::new();
        self.link.capture_app_session_state(&mut state);
        let time = self.link.clock_micros();
        state.set_tempo(tempo, time);
        self.link.commit_app_session_state(&state);
    }

    pub fn capture_app_state(&self) -> SessionState {
        let mut state = SessionState::new();
        self.link.capture_app_session_state(&mut state);
        state
    }
}
