use extension::Extension;

use libc::{c_void, size_t, uint8_t};

use std::io;
use std::mem;
use std::slice;

pub struct Folder<'a> {
    extension: &'a Extension,
    accumulator: *mut c_void,
}

impl<'a> Folder<'a> {
    /// Initialize the named folder.
    pub fn initialize(extension: &'a Extension, name: &[u8]) -> io::Result<Self> {
        unsafe {
            let mut error_message        = mem::uninitialized();
            let mut error_message_length = mem::uninitialized();
            let accumulator = (extension.folder_initialize)(
                name.as_ptr(),
                name.len(),
                &mut error_message,
                &mut error_message_length,
            );
            if accumulator.is_null() {
                return Err(Self::error(error_message, error_message_length));
            }
            Ok(Folder{extension, accumulator})
        }
    }

    /// Step the folder given an entry.
    pub fn step(&mut self, entry: &[u8]) -> io::Result<()> {
        unsafe {
            let mut error_message        = mem::uninitialized();
            let mut error_message_length = mem::uninitialized();
            let status = (self.extension.folder_step)(
                self.accumulator,
                entry.as_ptr(),
                entry.len(),
                &mut error_message,
                &mut error_message_length,
            );
            if status != 0 {
                return Err(Self::error(error_message, error_message_length));
            }
            Ok(())
        }
    }

    /// Finalize the folder, extracting the accumulator.
    pub fn finalize(&mut self) -> io::Result<&[u8]> {
        unsafe {
            let mut value                = mem::uninitialized();
            let mut value_length         = mem::uninitialized();
            let mut error_message        = mem::uninitialized();
            let mut error_message_length = mem::uninitialized();
            let status = (self.extension.folder_finalize)(
                self.accumulator,
                &mut value,
                &mut value_length,
                &mut error_message,
                &mut error_message_length,
            );
            if status != 0 {
                return Err(Self::error(error_message, error_message_length));
            }
            Ok(slice::from_raw_parts(value, value_length))
        }
    }

    unsafe fn error(message: *const uint8_t, length: size_t) -> io::Error {
        let error_kind = io::ErrorKind::Other;
        let error = slice::from_raw_parts(message, length);
        let error = String::from_utf8_lossy(error);
        io::Error::new(error_kind, error)
    }
}

impl<'a> Drop for Folder<'a> {
    fn drop(&mut self) {
        (self.extension.folder_free)(self.accumulator);
    }
}
