use crate::atomicmin::AtomicMin;
use crate::error::PngError;
use crate::PngResult;
use miniz_oxide::deflate::core::*;

pub(crate) fn compress_to_vec_oxipng(
    input: &[u8],
    level: u8,
    window_bits: i32,
    strategy: i32,
    max_size: &AtomicMin,
    deadline: &crate::Deadline,
) -> PngResult<Vec<u8>> {
    // The comp flags function sets the zlib flag if the window_bits parameter is > 0.
    let flags = create_comp_flags_from_zip_params(level.into(), window_bits, strategy);
    let mut compressor = CompressorOxide::new(flags);
    // if max size is known, then expect that much data (but no more than input.len())
    let mut output = Vec::with_capacity(max_size.get().unwrap_or(input.len() / 2).min(input.len()));
    // # Unsafe
    // We trust compress to not read the uninitialized bytes.
    unsafe {
        let cap = output.capacity();
        output.set_len(cap);
    }
    let mut in_pos = 0;
    let mut out_pos = 0;
    loop {
        let (status, bytes_in, bytes_out) = compress(
            &mut compressor,
            &input[in_pos..],
            &mut output[out_pos..],
            TDEFLFlush::Finish,
        );

        out_pos += bytes_out;
        in_pos += bytes_in;

        match status {
            TDEFLStatus::Done => {
                output.truncate(out_pos);
                break;
            }
            TDEFLStatus::Okay => {
                if let Some(max) = max_size.get() {
                    if output.len() > max {
                        return Err(PngError::DeflatedDataTooLong(output.len()));
                    }
                }
                if deadline.passed() {
                    return Err(PngError::TimedOut);
                }
                // We need more space, so extend the vector.
                if output.len().saturating_sub(out_pos) < 30 {
                    let current_len = output.len();
                    output.reserve(current_len);

                    // # Unsafe
                    // We trust compress to not read the uninitialized bytes.
                    unsafe {
                        let cap = output.capacity();
                        output.set_len(cap);
                    }
                }
            }
            // Not supposed to happen unless there is a bug.
            _ => panic!("Bug! Unexpectedly failed to compress!"),
        }
    }

    Ok(output)
}
