use std::mem;

use crate::{Error, ProcessedImage, RawImage, Result};
use libraw_sys as sys;

pub struct Processor {
    pub(crate) inner: *mut sys::libraw_data_t,
}

impl Processor {
    pub fn new() -> Self {
        let inner = unsafe { sys::libraw_init(0) };
        Self { inner }
    }

    fn open(&self, buf: &[u8]) -> Result<()> {
        Error::check(unsafe {
            sys::libraw_open_buffer(self.inner, buf.as_ptr() as *const _, buf.len())
        })?;
        Error::check(unsafe { sys::libraw_unpack(self.inner) })?;

        Ok(())
    }

    pub fn decode(self, buf: &[u8]) -> Result<RawImage> {
        self.open(buf)?;

        let decoded = RawImage::new(self);
        Ok(decoded)
    }

    #[inline]
    pub fn process_8bit(self, buf: &[u8]) -> Result<ProcessedImage<u8>> {
        self.process(buf)
    }

    #[inline]
    pub fn process_16bit(self, buf: &[u8]) -> Result<ProcessedImage<u16>> {
        self.process(buf)
    }

    fn process<T>(self, buf: &[u8]) -> Result<ProcessedImage<T>> {
        let bps = mem::size_of::<T>() * 8;
        debug_assert!(bps == 8 || bps == 16);
        unsafe { (*self.inner).params.output_bps = bps as i32 };

        self.open(buf)?;
        Error::check(unsafe { sys::libraw_dcraw_process(self.inner) })?;

        let mut result = 0i32;
        let processed = unsafe { sys::libraw_dcraw_make_mem_image(self.inner, &mut result) };
        Error::check(result)?;

        let processed = unsafe { ProcessedImage::from_raw(processed) };
        Ok(processed)
    }
}

impl Drop for Processor {
    fn drop(&mut self) {
        unsafe { sys::libraw_close(self.inner) }
    }
}

impl Default for Processor {
    fn default() -> Self {
        Self::new()
    }
}
