use std::mem::replace;

use anyhow::{Result, bail};
use cyberex::ipp;

use crate::{define::IndexPacket, packet::PacketEs};

pub type Cache = Vec<IndexPacket>;
pub struct Gops {
    cache: Cache,
    index: usize,
    gop_size: usize,
    gop_max: usize,
    is_last_key: bool,
}

impl Gops {
    pub fn new(gop_max: usize) -> Self {
        Self {
            cache: Vec::new(),
            index: 0,
            gop_size: 0,
            gop_max,
            is_last_key: false,
        }
    }

    pub fn input_data(&mut self, payload: Vec<PacketEs>, is_key: bool) -> Result<IndexPacket> {
        if payload.is_empty() {
            bail!("Payload is empty")
        }
        let index = ipp!(self.index);
        let pkt = IndexPacket { payload, index };

        let is_gop_start = self.is_gop_start(is_key);
        let gop_cache = &mut self.cache;

        if is_gop_start {
            if !gop_cache.is_empty() && self.gop_size == self.gop_max {
                gop_cache.clear();
                self.gop_size = 0;
            }
            self.gop_size += 1;
            Self::input_cache(gop_cache, &pkt, is_key, &mut self.is_last_key);

            return Ok(pkt);
        }
        if gop_cache.is_empty() {
            bail!("Gop cache is waiting for key frame to start")
        }
        Self::input_cache(gop_cache, &pkt, is_key, &mut self.is_last_key);
        Ok(pkt)
    }
    fn input_cache(cache: &mut Cache, pkt: &IndexPacket, is_key: bool, is_last_key: &mut bool) {
        cache.push(pkt.clone());
        let _ = replace(is_last_key, is_key);
    }

    fn is_gop_start(&self, is_key: bool) -> bool {
        is_key && !self.is_last_key
    }
    pub fn view(&self) -> &Vec<IndexPacket> {
        &self.cache
    }
}