use std::ffi::{c_char, CStr, CString};
use rsmpeg::avcodec::AVCodecContext;
use rsmpeg::avformat::AVFormatContextInput;
use rsmpeg::avutil::av_q2d;
use rsmpeg::ffi;
use anyhow::Context;
use rsmpeg::ffi::{av_version_info,avutil_license};

pub struct VideoMetadata{
    pub duration : i64,
    pub frame_rate: f64,
    pub width: i32,
    pub height: i32
}

impl VideoMetadata {
    pub fn get_metadata(path: &str) -> anyhow::Result<VideoMetadata> {
        let path = &CString::new(path.to_string())?;
        let input_format_context = AVFormatContextInput::open(path)?;
        let duration = input_format_context.duration;

        let (video_stream_index, decoder) = input_format_context
            .find_best_stream(ffi::AVMediaType_AVMEDIA_TYPE_VIDEO)?
            .context("Failed to find video stream")?;

        let video_stream = input_format_context
            .streams()
            .get(video_stream_index)
            .unwrap();

        let frame_rate = av_q2d(video_stream.r_frame_rate) as f64;

        // Get `width` and `height` from `decode_context`
        let mut decode_context = AVCodecContext::new(&decoder);
        decode_context.apply_codecpar(&video_stream.codecpar())?;
        decode_context.open(None)?;
        let width = decode_context.width;
        let height =   decode_context.height;

        let (video_stream_index, decoder) = input_format_context
            .find_best_stream(ffi::AVMediaType_AVMEDIA_TYPE_AUDIO)?
            .context("Failed to find audio stream")?;

        Ok(VideoMetadata{
            duration,
            frame_rate,
            width,
            height,
        })
    }

    pub fn get_version() -> String {
        let c_buf: *const c_char = unsafe { av_version_info() };
        let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) };
        let str_slice: &str = c_str.to_str().unwrap();
        str_slice.to_owned()
    }

    pub fn get_license() -> String {
        let c_buf: *const c_char = unsafe { avutil_license() };
        let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) };
        let str_slice: &str = c_str.to_str().unwrap();
        str_slice.to_owned()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_metadata_video_and_audio_file() {
        let metadata = VideoMetadata::get_metadata("tests/assets/video1.mp4").unwrap();
        assert_eq!(metadata.duration, 25406667);
        assert_eq!(metadata.frame_rate, 29.97002997002997);
        assert_eq!(metadata.width, 1920);
        assert_eq!(metadata.height, 1080);
    }
    #[test]
    fn test_get_metadata_video_only_file() {
        todo!()
    }

    #[test]
    fn test_get_metadata_audio_only_file() {
        todo!()
    }

    #[test]
    fn test_get_metadata_invalid_file() {
        todo!()
    }


    #[test]
    fn test_get_version() {
        assert!(!VideoMetadata::get_version().is_empty());
    }

    #[test]
    fn test_get_license() {
        assert!(!VideoMetadata::get_license().is_empty());
    }

}