use crate::player::{PlayerState, decoder::DecodedFrame};
use std::{
    collections::VecDeque,
    sync::{
        Arc, Mutex, MutexGuard,
        atomic::{AtomicU8, Ordering},
    },
};
use thiserror::Error;

#[derive(Debug, Error)]
#[error("deque error")]
pub enum ContextError {
    #[error("mutex is poisoned data is maybe inconsistent")]
    MutexPoisoned,
    #[error("out of memory")]
    OutOfMemory,
}

pub struct SharedVideoContext {
    player_state: Arc<AtomicU8>,
    deque: Arc<Mutex<VecDeque<DecodedFrame>>>,
}

impl SharedVideoContext {
    pub fn new(capacity: usize) -> Self {
        let deque = Arc::new(Mutex::new(VecDeque::with_capacity(capacity)));
        let player_state = Arc::new(AtomicU8::new(PlayerState::Init as u8));
        Self {
            player_state,
            deque,
        }
    }

    pub fn clone(&self) -> Self {
        Self {
            player_state: Arc::clone(&self.player_state),
            deque: Arc::clone(&self.deque),
        }
    }

    pub fn player_state(&self) -> PlayerState {
        match self.player_state.load(Ordering::Relaxed) {
            0 => PlayerState::Init,
            1 => PlayerState::Playing,
            2 => PlayerState::Stopped,
            3 => PlayerState::DecoderFinished,
            other => panic!("unknown player state: {other}"),
        }
    }

    pub fn is_finished(&self) -> bool {
        matches!(self.player_state(), PlayerState::DecoderFinished)
    }

    pub fn is_stopped(&self) -> bool {
        matches!(self.player_state(), PlayerState::Stopped)
    }

    pub fn set_state(&self, state: PlayerState) {
        self.player_state.store(state as u8, Ordering::Relaxed);
    }

    pub fn push_frame(&self, frame: DecodedFrame) -> Result<(), ContextError> {
        let mut deque = self.deque.lock().map_err(|_| ContextError::MutexPoisoned)?;
        if deque.len() >= deque.capacity() {
            Err(ContextError::OutOfMemory)
        } else {
            deque.push_back(frame);
            Ok(())
        }
    }

    pub fn with_deque_mut<F, R>(&self, func: F) -> Result<R, ContextError>
    where
        F: FnOnce(MutexGuard<'_, VecDeque<DecodedFrame>>) -> R,
    {
        let deque = self.deque.lock().map_err(|_| ContextError::MutexPoisoned)?;
        Ok(func(deque))
    }

    pub fn has_frames(&self) -> Result<bool, ContextError> {
        let deque = self.deque.lock().map_err(|_| ContextError::MutexPoisoned)?;
        Ok(!deque.is_empty())
    }

    pub fn is_full(&self) -> Result<bool, ContextError> {
        let deque = self.deque.lock().map_err(|_| ContextError::MutexPoisoned)?;
        Ok(deque.len() >= deque.capacity())
    }

    pub fn clear(&self) -> Result<(), ContextError> {
        let mut deque = self.deque.lock().map_err(|_| ContextError::MutexPoisoned)?;
        deque.clear();

        self.player_state
            .store(PlayerState::Init as u8, Ordering::Relaxed);

        Ok(())
    }
}
