/// Implementation using [core graphics](https://crates.rs/crates/core-graphics).
///
use objc::*;
use core_foundation::data::*;
use core_graphics::image::*;
use core_graphics::color_space::*;
use core_graphics::geometry::*;
use core_graphics::context::*;
use core_graphics::base::*;
use imgref::*;
use rgb::*;
use crate::error::*;
use objc::runtime::*;

/// Returns an RGBA 8-bit bitmap in sRGB color space with premultiplied alpha.
///
/// See [`ImgRef`](https://crates.rs/crates/rgb) for instructions how to use the returned bitmap.
pub fn decode_image_as_rgba_premultiplied(image_data: &[u8]) -> Result<ImgVec<RGBA8>, Error> {
    unsafe {
        let pool = AutoreleasePool(msg_send![Class::get("NSAutoreleasePool").ok_or(Error::ObjcError)?, new]);
        let data = CFData::from_buffer(image_data);
        let rep: *mut Object = msg_send![Class::get("NSBitmapImageRep").ok_or(Error::ObjcError)?, imageRepWithData:data];
        if rep.is_null() {
            return Err(Error::DecodeError);
        }
        let wow_this_is_a_dodgy_hack: &CGImageRef = msg_send![rep, CGImage];
        let image = wow_this_is_a_dodgy_hack.to_owned();

        let width = image.width();
        let height = image.height();
        let mut pixel_data = vec![RGBA8::new(0,0,0,0); width * height];
        let colorspace = CGColorSpace::create_with_name(kCGColorSpaceSRGB).ok_or(Error::ObjcError)?;
        let context = CGContext::create_bitmap_context(Some(pixel_data.as_mut_ptr() as *mut _),
            width, height,
            8, width * 4,
            &colorspace,
            kCGImageAlphaPremultipliedLast);
        context.draw_image(CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(width as CGFloat, height as CGFloat)), &image);
        drop(pool);
        Ok(ImgVec::new(pixel_data, width, height))
    }
}

pub fn decode_image_as_rgba(image_data: &[u8]) -> Result<ImgVec<RGBA8>, Error> {
    // macOS is so silly it doesn't have non-premultiplied bitmaps
    let mut img = decode_image_as_rgba_premultiplied(image_data)?;
    for px in img.pixels_mut() {
        if px.a > 0 && px.a != 255 {
            *px = RGBA8 {
                r: (px.r as u16 * 255 / px.a as u16) as u8,
                g: (px.g as u16 * 255 / px.a as u16) as u8,
                b: (px.b as u16 * 255 / px.a as u16) as u8,
                a: px.a,
            }
        }
    }
    Ok(img)
}

#[link(name = "AppKit", kind = "framework")]
extern {
}

struct AutoreleasePool(*mut Object);
impl Drop for AutoreleasePool {
    fn drop(&mut self) {
        unsafe {
            let _: () = msg_send![self.0, drain];
            self.0 = ::std::ptr::null_mut();
        }
    }
}

#[test]
fn read_test_png_file() {
    let img = decode_image_as_rgba_premultiplied(&std::fs::read("test.png").expect("test.png")).expect("decoded");
    assert_eq!(2, img.width());
    assert_eq!(2, img.height());
    assert_eq!(RGBA8::new(0,0,0,255), img.buf()[0]);
    assert_eq!(RGBA8::new(255,0,0,255), img.buf()[1]);
    assert_eq!(RGBA8::new(0,0,0,0), img.buf()[2]);
    assert_eq!(RGBA8::new(0,0,0,0), img.buf()[3]);
}
