use std::collections::VecDeque;
use rsmpeg::error::RsmpegError;
use rsmpeg::error::RsmpegError::AVError;
use std::sync::Condvar;
use rsmpeg::avcodec::AVPacket;
use crate::playback::mutex::{MutexQueue, New};

pub struct MyAVPacketList {
    pub pkt : Box<AVPacket>,
    pub next: Option<Box<MyAVPacketList>>,
    pub serial: i64,
}

impl Default for MyAVPacketList {
    fn default() -> Self {
        MyAVPacketList {
            pkt: Default::default(),
            next: Default::default(),
            serial: Default::default(),
        }
    }
}

impl MyAVPacketList {
    fn new(pkt: AVPacket, serial: i64) -> Self {
        MyAVPacketList {
            pkt: Box::new(pkt),
            next: None,
            serial,
        }
    }
}

pub type MutexPacketQueue = MutexQueue<PacketQueue>;

pub struct PacketQueue {
    pub queue : VecDeque<MyAVPacketList>,
    pub size: i64,
    pub duration: i64,
    pub abort_request: bool,
    pub serial: i64,
}

impl New for PacketQueue {
    fn new() -> PacketQueue {
        PacketQueue {
            queue: VecDeque::new(),
            size: 0,
            duration: 0,
            abort_request: false,
            serial: 0,
        }
    }
}


impl PacketQueue {
    /**
     * Put the given AVPacket in the given PacketQueue.
     *
     * @param  queue    the queue to be used for the insert
     * @param  packet   the AVPacket to be inserted in the queue
     *
     * @return          0 if the AVPacket is correctly inserted in the given PacketQueue.
     */

    fn put(&mut self, cond: &Condvar, packet: AVPacket) ->  Result<(), RsmpegError>
    {
        if self.abort_request
        {
            return Err(AVError(-1));
        }
        let mut pkt = MyAVPacketList::new(packet, self.serial);
        let pkt_duration = pkt.pkt.duration;
        let pkt_size : i64 = pkt.pkt.size.into();
        //if (packet == &flush_pkt) // TODO
        //  queue->serial++;

        self.queue.push_back(pkt);
        // queue->size += pkt1->pkt.size + sizeof(*pkt1);
        // queue->duration += pkt1->pkt.duration;
        self.size += pkt_size; // TODO
        self.duration += pkt_duration;

        /* XXX: should duplicate packet data in DV case */
        cond.notify_one();
        return Ok(());
    }

}

impl MutexPacketQueue {
    pub  fn put_nullpacket(&mut self, stream_index: i64) -> Result<(), RsmpegError>
    {
        let mut pkt = AVPacket::new();
        pkt.set_stream_index(stream_index as i32);
        self.put(pkt)
    }

    /// Puts the given AVPacket in the given PacketQueue.
    ///
    /// param : `q`  the packet queue to be used for the insert
    /// param : `packet` the AVPacket to be inserted in the queue
    ///
    /// return  : true if the AVPacket is correctly inserted in the given PacketQueue.

    fn put(&mut self, packet: AVPacket) -> Result<(), RsmpegError>
    {
        //SDL_LockMutex(queue->mutex);
        let _pq = self.mutex.lock().unwrap();
        let ret = self.data.put(&self.cond, packet);
        //SDL_UnlockMutex(queue->mutex);
        // if (packet != &flush_pkt && ret < 0) // TODO
        //    av_packet_unref(packet);
        ret
    }



    /// Flush the given packet queue
    fn flush(&mut self)
    {
        //SDL_LockMutex(q->mutex);
        let _m = self.mutex.lock().unwrap();
        let pq = &mut self.data;
        pq.queue.clear();
        pq.size = 0;
        pq.duration = 0;
        pq.serial += pq.serial;
        //SDL_UnlockMutex(q->mutex);
    }

    fn destroy(&mut self)
    {
        self.flush();
    }

    fn abort(q: &mut MutexPacketQueue)
    {
        //SDL_LockMutex(q->mutex);
        let _m = q.mutex.lock().unwrap();
        let pq = &mut q.data;
        pq.abort_request = true;
        //SDL_CondSignal(q->cond);
        q.cond.notify_one()
        //SDL_UnlockMutex(q->mutex);
    }

    fn start(q: &mut MutexPacketQueue)
    {
        //SDL_LockMutex(q->mutex);
        let _m = q.mutex.lock().unwrap();
        let pq = &mut q.data;
        pq.abort_request = false;
        pq.serial += 1;
        //SDL_UnlockMutex(q->mutex);
    }


    pub fn get(&mut self, packet: &mut AVPacket, block : bool, serial: Option<&mut Box<i64>>) -> Result<(),RsmpegError>
    {
        // SDL_LockMutex(q->mutex);
        let mut m = self.mutex.lock().unwrap();
        let mut pq = &mut self.data;
        let mut ret = Ok(());
        loop {
            if pq.abort_request {
                ret = Err(AVError(-1));
                break;
            }
            let pkt_opt = pq.queue.pop_front();
            if let Some (pkt) = pkt_opt {
                pq.size = pkt.pkt.as_ref().size.into();
                *packet = *pkt.pkt;
                if let Some(mut s) = serial {
                    *s = Box::new(pkt.serial)
                }
                ret = Ok(());

                break;
            } else if block {
                ret = Ok(());
                break;
            } else {
                m = self.cond.wait(m).unwrap();
            }
        }
        ret
    }


}


