//! Small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images.
//! It's powering [pngquant2](https://pngquant.org).
//!
//! This is a low-level crate exposing a C API. If you're looking for a Rust library, see [imagequant](https://crates.io/crates/imagequant).
//!
//! ## License
//!
//! Libimagequant is dual-licensed:
//!
//! * For Free/Libre Open Source Software it's available under [GPL v3 or later](https://raw.github.com/ImageOptim/libimagequant/master/COPYRIGHT) with additional copyright notices for older parts of the code.
//!
//! * For use in non-GPL software (e.g. closed-source or App Store distribution) please ask kornel@pngquant.org for a commercial license.
//!
//! ## Overview
//!
//! The basic flow is:
//!
//! 1. Create attributes object and configure the library.
//! 2. Create image object from RGBA pixels or data source.
//! 3. Perform quantization (generate palette).
//! 4. Store remapped image and final palette.
//! 5. Free memory.
//!
//! Please note that libimagequant only handles raw uncompressed arrays of pixels in memory and is completely independent of any file format.
//!
//! There are 3 ways to create image object for quantization:
//!
//!   * `liq_image_create_rgba()` for simple, contiguous RGBA pixel arrays (width×height×4 bytes large bitmap).
//!   * `liq_image_create_rgba_rows()` for non-contiguous RGBA pixel arrays (that have padding between rows or reverse order, e.g. BMP).
//!   * `liq_image_create_custom()` for RGB, ABGR, YUV and all other formats that can be converted on-the-fly to RGBA (you have to supply the conversion function).
//!
//! Note that "image" here means raw uncompressed pixels. If you have a compressed image file, such as PNG, you must use another library (e.g. lodepng) to decode it first.

#![allow(non_camel_case_types)]

#[cfg(feature = "openmp")]
extern crate openmp_sys;

use std::os::raw::{c_int, c_uint, c_char, c_void};
use std::error;
use std::fmt;

pub enum liq_attr {}
pub enum liq_image {}
pub enum liq_result {}
pub enum liq_histogram {}
pub type liq_color = rgb::RGBA8;

#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum liq_error {
    LIQ_OK = 0,
    LIQ_QUALITY_TOO_LOW = 99,
    LIQ_VALUE_OUT_OF_RANGE = 100,
    LIQ_OUT_OF_MEMORY,
    LIQ_ABORTED,
    LIQ_BITMAP_NOT_AVAILABLE,
    LIQ_BUFFER_TOO_SMALL,
    LIQ_INVALID_POINTER,
    LIQ_UNSUPPORTED,
}

bitflags::bitflags! {
    #[repr(C)]
    pub struct liq_ownership: c_int {
        /// Moves ownership of the rows array. It will free it using `free()` or custom allocator.
        const LIQ_OWN_ROWS = 4;
        /// Moves ownership of the pixel data. It will free it using `free()` or custom allocator.
        const LIQ_OWN_PIXELS = 8;
        /// Makes a copy of the pixels, so the `liq_image` is not tied to pixel's lifetime.
        const LIQ_COPY_PIXELS = 16;
    }
}

#[repr(C)]
pub struct liq_palette {
    pub count: c_int,
    pub entries: [liq_color; 256],
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct liq_histogram_entry {
    pub color: liq_color,
    pub count: c_uint,
}

impl error::Error for liq_error {
}

impl fmt::Display for liq_error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match *self {
            liq_error::LIQ_OK => "OK",
            liq_error::LIQ_QUALITY_TOO_LOW => "QUALITY_TOO_LOW",
            liq_error::LIQ_VALUE_OUT_OF_RANGE => "VALUE_OUT_OF_RANGE",
            liq_error::LIQ_OUT_OF_MEMORY => "OUT_OF_MEMORY",
            liq_error::LIQ_ABORTED => "ABORTED",
            liq_error::LIQ_BITMAP_NOT_AVAILABLE => "BITMAP_NOT_AVAILABLE",
            liq_error::LIQ_BUFFER_TOO_SMALL => "BUFFER_TOO_SMALL",
            liq_error::LIQ_INVALID_POINTER => "INVALID_POINTER",
            liq_error::LIQ_UNSUPPORTED => "UNSUPPORTED",
        })
    }
}

impl liq_error {
    #[inline]
    pub fn is_ok(&self) -> bool {
        *self == liq_error::LIQ_OK
    }

    pub fn is_err(&self) -> bool {
        !self.is_ok()
    }

    #[inline]
    pub fn ok(&self) -> Result<(), liq_error> {
        match *self {
            liq_error::LIQ_OK => Ok(()),
            e => Err(e),
        }
    }

    #[inline]
    pub fn ok_or<E>(self, err: E) -> Result<(), E> {
        if self.is_ok() {
            Ok(())
        } else {
            Err(err)
        }
    }

    pub fn unwrap(&self) {
        assert!(self.is_ok(), "{}", self);
    }

    pub fn expect(&self, msg: &str) {
        assert!(self.is_ok(), "{}", msg);
    }
}

pub type liq_log_callback_function = Option<unsafe extern "C" fn(arg1: &liq_attr, message: *const c_char, user_info: *mut c_void)>;
pub type liq_log_flush_callback_function = Option<unsafe extern "C" fn(arg1: &liq_attr, user_info: *mut c_void)>;
pub type liq_progress_callback_function = Option<unsafe extern "C" fn(progress_percent: f32, user_info: *mut c_void) -> c_int>;
pub type liq_image_get_rgba_row_callback = unsafe extern "C" fn(row_out: *mut liq_color, row: c_int, width: c_int, user_info: *mut c_void);

#[link(name="imagequant", kind="static")]
extern "C" {

    /// Returns object that will hold initial settings (attributes) for the library.
    ///
    /// The object should be freed using `liq_attr_destroy()` after it's no longer needed.
    /// Returns `NULL` in the unlikely case that the library cannot run on the current machine (e.g. the library has been compiled for SSE-capable x86 CPU and run on VIA C3 CPU).
    pub fn liq_attr_create() -> *mut liq_attr;
    pub fn liq_attr_create_with_allocator(malloc: unsafe extern "C" fn(usize) -> *mut c_void, free: unsafe extern "C" fn(*mut c_void)) -> *mut liq_attr;
    pub fn liq_attr_copy(orig: &liq_attr) -> *mut liq_attr;
    pub fn liq_attr_destroy(attr: &mut liq_attr);

    /// Specifies maximum number of colors to use.
    ///
    /// The default is 256. Instead of setting a fixed limit it's better to use `liq_set_quality()`.
    /// The first argument is attributes object from `liq_attr_create()`.
    /// Returns `LIQ_VALUE_OUT_OF_RANGE` if number of colors is outside the range 2-256.
    pub fn liq_set_max_colors(attr: &mut liq_attr, colors: c_int) -> liq_error;
    pub fn liq_get_max_colors(attr: &liq_attr) -> c_int;
    pub fn liq_set_speed(attr: &mut liq_attr, speed: c_int) -> liq_error;
    pub fn liq_get_speed(attr: &liq_attr) -> c_int;
    pub fn liq_set_min_posterization(attr: &mut liq_attr, bits: c_int) -> liq_error;
    pub fn liq_get_min_posterization(attr: &liq_attr) -> c_int;
    /// Quality is in range `0` (worst) to `100` (best) and values are analoguous to JPEG quality (i.e. `80` is usually good enough).
    ///
    /// Quantization will attempt to use the lowest number of colors needed to achieve `maximum` quality. `maximum` value of `100` is the default and means conversion as good as possible.
    /// If it's not possible to convert the image with at least `minimum` quality (i.e. 256 colors is not enough to meet the minimum quality), then `liq_image_quantize()` will fail. The default minumum is `0` (proceeds regardless of quality).
    /// Quality measures how well the generated palette fits image given to `liq_image_quantize()`. If a different image is remapped with `liq_write_remapped_image()` then actual quality may be different.
    /// Regardless of the quality settings the number of colors won't exceed the maximum (see `liq_set_max_colors()`).
    /// The first argument is attributes object from `liq_attr_create()`.
    ///
    /// Returns `LIQ_VALUE_OUT_OF_RANGE` if target is lower than minimum or any of them is outside the 0-100 range.
    /// Returns `LIQ_INVALID_POINTER` if `attr` appears to be invalid.
    pub fn liq_set_quality(attr: &mut liq_attr, minimum: c_int, maximum: c_int) -> liq_error;
    pub fn liq_get_min_quality(attr: &liq_attr) -> c_int;
    pub fn liq_get_max_quality(attr: &liq_attr) -> c_int;
    pub fn liq_set_last_index_transparent(attr: &mut liq_attr, is_last: c_int);

    pub fn liq_image_create_rgba_rows(attr: &liq_attr, rows: *const *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
    /// Creates an object that represents the image pixels to be used for quantization and remapping.
    ///
    /// The pixel array must be contiguous run of RGBA pixels (alpha is the last component, 0 = transparent, 255 = opaque).
    ///
    /// The first argument is attributes object from `liq_attr_create()`. The same `attr` object should be used for the entire process, from creation of images to quantization.
    ///
    /// The `pixels` array must not be modified or freed until this object is freed with `liq_image_destroy()`. See also `liq_image_set_memory_ownership()`.
    ///
    /// `width` and `height` are dimensions in pixels. An image 10x10 pixel large will need a 400-byte array.
    ///
    /// `gamma` can be `0` for images with the typical 1/2.2 [gamma](https://en.wikipedia.org/wiki/Gamma_correction).
    /// Otherwise `gamma` must be > 0 and < 1, e.g. `0.45455` (1/2.2) or `0.55555` (1/1.8). Generated palette will use the same gamma unless `liq_set_output_gamma()` is used. If `liq_set_output_gamma` is not used, then it only affects whether brighter or darker areas of the image will get more palette colors allocated.
    ///
    /// Returns `NULL` on failure, e.g. if `pixels` is `NULL` or `width`/`height` is <= 0.
    pub fn liq_image_create_rgba(attr: &liq_attr, bitmap: *const u8, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
    /// unsafe: It will crash if the owned memory wasn't allocated using `libc::malloc()` (or whatever allocator C side is using)
    pub fn liq_image_set_memory_ownership(image: &liq_image, own: liq_ownership) -> liq_error;

    pub fn liq_set_log_callback(arg1: &mut liq_attr, arg2: liq_log_callback_function, user_info: *mut c_void);
    pub fn liq_set_log_flush_callback(arg1: &mut liq_attr, arg2: liq_log_flush_callback_function, user_info: *mut c_void);
    pub fn liq_attr_set_progress_callback(arg1: &mut liq_attr, arg2: liq_progress_callback_function, user_info: *mut c_void);
    pub fn liq_result_set_progress_callback(arg1: &mut liq_result, arg2: liq_progress_callback_function, user_info: *mut c_void);
    pub fn liq_image_create_custom(attr: &liq_attr, row_callback: liq_image_get_rgba_row_callback, user_info: *mut c_void, width: c_int, height: c_int, gamma: f64) -> *mut liq_image;
    /// Remap assuming the image will be always presented exactly on top of this background.
    ///
    /// Takes ownership of the background image (i.e. do NOT use it afterwards, do NOT call liq_image_destroy on it).
    ///
    /// The background must have the same size. The foreground image must have a transparent color.
    pub fn liq_image_set_background(img: &mut liq_image, background: *mut liq_image) -> liq_error;
    pub fn liq_image_set_importance_map(img: &mut liq_image, buffer: *mut u8, buffer_size: usize, own: liq_ownership) -> liq_error;
    pub fn liq_image_add_fixed_color(img: &mut liq_image, color: liq_color) -> liq_error;
    pub fn liq_image_get_width(img: &liq_image) -> c_int;
    pub fn liq_image_get_height(img: &liq_image) -> c_int;
    pub fn liq_image_destroy(img: &mut liq_image);

    pub fn liq_histogram_create(attr: &liq_attr) -> *mut liq_histogram;
    pub fn liq_histogram_add_image(hist: &mut liq_histogram, attr: &liq_attr, image: &mut liq_image) -> liq_error;
    pub fn liq_histogram_add_colors(hist: &mut liq_histogram, attr: &liq_attr, entries: *const liq_histogram_entry, num_entries: c_int, gamma: f64) -> liq_error;
    pub fn liq_histogram_add_fixed_color(hist: &mut liq_histogram, color: liq_color) -> liq_error;
    pub fn liq_histogram_destroy(hist: &mut liq_histogram);

    /// Performs quantization (palette generation) based on settings in `attr` (from `liq_attr_create()`) and pixels of the image.
    ///
    /// Returns `LIQ_OK` if quantization succeeds and sets `liq_result` pointer in `out_result`. The last argument is used for receiving the `result` object:
    ///
    ///     liq_result *result;
    ///     if (LIQ_OK == liq_image_quantize(img, attr, &result)) { // Note &result
    ///         // result pointer is valid here
    ///     }
    ///
    /// Returns `LIQ_QUALITY_TOO_LOW` if quantization fails due to limit set in `liq_set_quality()`.
    ///
    /// See `liq_write_remapped_image()`.
    ///
    /// If you want to generate one palette for multiple images at once, see `liq_histogram_create()`.
    pub fn liq_quantize_image(options: &liq_attr, input_image: &liq_image) -> *mut liq_result;
    pub fn liq_histogram_quantize(input_hist: &liq_histogram, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error;
    pub fn liq_image_quantize(input_image: &liq_image, options: &liq_attr, result_output: &mut *mut liq_result) -> liq_error;
    /// Enables/disables dithering in `liq_write_remapped_image()`.
    ///
    /// Dithering level must be between `0` and `1` (inclusive). Dithering level `0` enables fast non-dithered remapping. Otherwise a variation of Floyd-Steinberg error diffusion is used.
    ///
    /// Precision of the dithering algorithm depends on the speed setting, see `liq_set_speed()`.
    ///
    /// Returns `LIQ_VALUE_OUT_OF_RANGE` if the dithering level is outside the 0-1 range.
    pub fn liq_set_dithering_level(res: &mut liq_result, dither_level: f32) -> liq_error;
    pub fn liq_set_output_gamma(res: &mut liq_result, gamma: f64) -> liq_error;
    pub fn liq_get_output_gamma(result: &liq_result) -> f64;
    /// Returns pointer to palette optimized for image that has been quantized or remapped
    /// (final refinements are applied to the palette during remapping).
    ///
    /// It's valid to call this method before remapping, if you don't plan to remap any images or want to use same palette for multiple images.
    ///
    /// `liq_palette->count` contains number of colors (up to 256), `liq_palette->entries[n]` contains RGBA value for nth palette color.
    pub fn liq_get_palette<'a>(result: &'a mut liq_result) -> &'a liq_palette;
    /// Remaps the image to palette and writes its pixels to the given buffer, 1 pixel per byte.
    ///
    /// The buffer must be large enough to fit the entire image, i.e. width×height bytes large. For safety, pass the size of the buffer as `buffer_size`.
    ///
    /// For best performance call `liq_get_palette()` *after* this function, as palette is improved during remapping (except when `liq_histogram_quantize()` is used).
    ///
    /// Returns `LIQ_BUFFER_TOO_SMALL` if given size of the buffer is not enough to fit the entire image.
    ///
    ///     int buffer_size = width*height;
    ///     char *buffer = malloc(buffer_size);
    ///     if (LIQ_OK == liq_write_remapped_image(result, input_image, buffer, buffer_size)) {
    ///         liq_palette *pal = liq_get_palette(result);
    ///         // save image
    ///     }
    ///
    /// See `liq_get_palette()`.
    ///
    /// The buffer is assumed to be contiguous, with rows ordered from top to bottom, and no gaps between rows. If you need to write rows with padding or upside-down order, then use `liq_write_remapped_image_rows()`.
    ///
    /// Please note that it only writes raw uncompressed pixels to memory. It does not perform any PNG compression. If you'd like to create a PNG file then you need to pass the raw pixel data to another library, e.g. libpng or lodepng. See `rwpng.c` in `pngquant` project for an example how to do that.
    pub fn liq_write_remapped_image(result: &mut liq_result, input_image: &mut liq_image, buffer: *mut u8, buffer_size: usize) -> liq_error;
    pub fn liq_write_remapped_image_rows(result: &mut liq_result, input_image: &mut liq_image, row_pointers: *const *mut u8) -> liq_error;
    pub fn liq_get_quantization_error(result: &liq_result) -> f64;
    pub fn liq_get_quantization_quality(result: &liq_result) -> c_int;

    pub fn liq_result_destroy(res: &mut liq_result);
    pub fn liq_get_remapping_error(result: &liq_result) -> f64;
    pub fn liq_get_remapping_quality(result: &liq_result) -> c_int;
    pub fn liq_version() -> c_int;
}

#[test]
fn ownership_bitflags() {
    assert_eq!(4+16, (liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_COPY_PIXELS).bits());
}

#[test]
fn links_and_runs() {
    use std::ptr;
    unsafe {
        assert!(liq_version() >= 20901);
        let attr = liq_attr_create();
        assert!(!attr.is_null());
        let hist = liq_histogram_create(&*attr);
        assert!(!hist.is_null());
        let mut res = ptr::null_mut();
        liq_histogram_quantize(&*hist, &*attr, &mut res);
        assert!(res.is_null());
        liq_histogram_destroy(&mut *hist);
        liq_attr_destroy(&mut *attr);
    }
}
