use std::ffi::c_void;

/// Integration tests on the nvTIFF
/// [Parser API](https://docs.nvidia.com/cuda/nvtiff/userguide.html#parser-api-reference)
use nvtiff_sys::{
    nvtiffImageInfo, nvtiffImageType, nvtiffPhotometricInt, nvtiffPlanarConfig, nvtiffSampleFormat,
    nvtiffStatus_t, nvtiffStream, nvtiffStreamCreate, nvtiffStreamGetImageInfo,
    nvtiffStreamGetNumImages, nvtiffStreamGetTagValue, nvtiffStreamParse,
    nvtiffStreamParseFromFile, nvtiffTag,
};

#[test]
fn test_nvtiff_parse_from_bytes() {
    // Step 0: Create bitstream
    let mut stream = std::mem::MaybeUninit::uninit();
    let mut tiff_stream: *mut nvtiffStream = stream.as_mut_ptr();

    let status_create: nvtiffStatus_t::Type = unsafe { nvtiffStreamCreate(&mut tiff_stream) };
    dbg!(status_create); // should be 0=SUCCESS
    assert_eq!(status_create, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);

    // Step 1: Get bytes from HTTP request
    let tif_url: &str =
        "https://download.osgeo.org/geotiff/samples/GeogToWGS84GeoKey/GeogToWGS84GeoKey5.tif";
    let response = minreq::get(tif_url)
        .send()
        .expect("GET request for {tif_url} failed");
    let bytes = response.as_bytes();

    // Step 2: Parse the TIFF data from byte stream buffer
    let status_parse: u32 = unsafe { nvtiffStreamParse(bytes.as_ptr(), usize::MAX, tiff_stream) };
    dbg!(status_parse); // should be 0=SUCCESS
    assert_eq!(status_parse, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);

    // Step 3: Extract file-level metadata information from the TIFF stream
    let mut num_images: u32 = 0;
    let status_numimages: u32 = unsafe { nvtiffStreamGetNumImages(tiff_stream, &mut num_images) };
    dbg!(status_numimages); // should be 0=SUCCESS
    assert_eq!(status_numimages, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);
    dbg!(num_images);
    assert_eq!(num_images, 1);

    // Step 4: Extract image-level metadata information from the TIFF stream
    let mut image_info = nvtiffImageInfo::default();
    let status_imageinfo: u32 =
        unsafe { nvtiffStreamGetImageInfo(tiff_stream, 0, &raw mut image_info) };
    assert_eq!(status_imageinfo, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);
    dbg!(image_info);
    assert_eq!(
        image_info,
        nvtiffImageInfo {
            image_type: nvtiffImageType::NVTIFF_IMAGETYPE_PAGE, // 2
            image_width: 101,
            image_height: 101,
            compression: 32773, // nvtiffCompression::NVTIFF_COMPRESSION_PACKBITS
            photometric_int: nvtiffPhotometricInt::NVTIFF_PHOTOMETRIC_PALETTE, // 3
            planar_config: nvtiffPlanarConfig::NVTIFF_PLANARCONFIG_CONTIG, // 1
            samples_per_pixel: 1,
            bits_per_pixel: 8,
            bits_per_sample: [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            sample_format: [
                nvtiffSampleFormat::NVTIFF_SAMPLEFORMAT_UINT, // 1
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0,
                0
            ],
        }
    );
}

#[test]
fn nvtiff_get_tag_info() {
    // Step 0: Create bitstream
    let mut stream = std::mem::MaybeUninit::uninit();
    let mut tiff_stream: *mut nvtiffStream = stream.as_mut_ptr();

    let status_create: nvtiffStatus_t::Type = unsafe { nvtiffStreamCreate(&raw mut tiff_stream) };
    dbg!(status_create); // should be 0=SUCCESS
    assert_eq!(status_create, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);

    // Step 1: Parse the TIFF file from disk
    let tiff_cstr = std::ffi::CString::new("images/cog_dateline.tif").unwrap();
    let tiff_path: *const std::os::raw::c_char = tiff_cstr.as_ptr();

    let status_parse: u32 = unsafe { nvtiffStreamParseFromFile(tiff_path, tiff_stream) };
    dbg!(status_parse); // should be 0=SUCCESS
    assert_eq!(status_parse, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);

    // Step 2: Extract Model tag metadata information from the TIFF stream
    // https://docs.ogc.org/is/19-008r4/19-008r4.html#_coordinate_transformations

    // Step 2.1: Parse ModelPixelScaleTag
    let mut pixel_scale: Vec<f64> = vec![f64::NAN; 3];
    let status_pixelscaleinfo: u32 = unsafe {
        nvtiffStreamGetTagValue(
            tiff_stream,
            0,
            nvtiffTag::NVTIFF_TAG_MODEL_PIXEL_SCALE,
            pixel_scale.as_mut_ptr().cast::<c_void>(),
            3,
        )
    };
    dbg!(status_pixelscaleinfo); // should be 0=SUCCESS
    assert_eq!(status_pixelscaleinfo, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);
    dbg!(&pixel_scale);
    assert_eq!(
        pixel_scale,
        [100.012_903_225_806_45, 100.012_738_853_503_18, 0.0] // x_scale, y_scale, z_scale
    );

    // Step 2.2: Parse ModelTiepointTag
    let mut tie_points: Vec<f64> = vec![f64::NAN; 6];
    let status_tiepointsinfo: u32 = unsafe {
        nvtiffStreamGetTagValue(
            tiff_stream,
            0,
            nvtiffTag::NVTIFF_TAG_MODEL_TIE_POINT,
            tie_points.as_mut_ptr().cast::<c_void>(),
            6,
        )
    };
    dbg!(status_tiepointsinfo); // should be 0=SUCCESS
    assert_eq!(status_tiepointsinfo, nvtiffStatus_t::NVTIFF_STATUS_SUCCESS);
    dbg!(&tie_points);
    assert_eq!(
        tie_points,
        [
            0.0,                   // i
            0.0,                   // j
            0.0,                   // k
            570_635.006_451_612_9, // x_origin
            5_847_564.993_630_573, // y_origin
            0.0                    // z_origin
        ]
    );

    // Step 2.3: Parse ModelTransformationTag
    let mut model_transformation: Vec<f64> = vec![f64::NAN; 16];
    let status_modeltransformationinfo: u32 = unsafe {
        nvtiffStreamGetTagValue(
            tiff_stream,
            0,
            nvtiffTag::NVTIFF_TAG_MODEL_TRANSFORMATION,
            model_transformation.as_mut_ptr().cast::<c_void>(),
            16,
        )
    };
    dbg!(status_modeltransformationinfo); // should be 11=TAG_NOT_FOUND
    assert_eq!(
        status_modeltransformationinfo,
        nvtiffStatus_t::NVTIFF_STATUS_TAG_NOT_FOUND
    );
}
