use crate::{
    deflate::{BlockState, DeflateStream, Strategy},
    DeflateFlush,
};

use self::{huff::deflate_huff, rle::deflate_rle, stored::deflate_stored};

mod fast;
mod huff;
mod medium;
mod quick;
mod rle;
mod slow;
mod stored;

#[macro_export]
macro_rules! flush_block {
    ($stream:expr, $is_last_block:expr) => {
        $crate::deflate::flush_block_only($stream, $is_last_block);

        if $stream.avail_out == 0 {
            return match $is_last_block {
                true => BlockState::FinishStarted,
                false => BlockState::NeedMore,
            };
        }
    };
}

pub fn run(stream: &mut DeflateStream, flush: DeflateFlush) -> BlockState {
    match stream.state.strategy {
        _ if stream.state.level == 0 => deflate_stored(stream, flush),
        Strategy::HuffmanOnly => deflate_huff(stream, flush),
        Strategy::Rle => deflate_rle(stream, flush),
        Strategy::Default | Strategy::Filtered | Strategy::Fixed => {
            (CONFIGURATION_TABLE[stream.state.level as usize].func)(stream, flush)
        }
    }
}

type CompressFunc = fn(&mut DeflateStream, flush: DeflateFlush) -> BlockState;

pub struct Config {
    pub good_length: u16, /* reduce lazy search above this match length */
    pub max_lazy: u16,    /* do not perform lazy search above this match length */
    pub nice_length: u16, /* quit search above this match length */
    pub max_chain: u16,
    pub func: CompressFunc,
}

impl Config {
    const fn new(
        good_length: u16,
        max_lazy: u16,
        nice_length: u16,
        max_chain: u16,
        func: CompressFunc,
    ) -> Self {
        Self {
            good_length,
            max_lazy,
            nice_length,
            max_chain,
            func,
        }
    }
}

pub const CONFIGURATION_TABLE: [Config; 10] = {
    [
        Config::new(0, 0, 0, 0, stored::deflate_stored), // 0 /* store only */
        Config::new(0, 0, 0, 0, quick::deflate_quick),   // 1
        Config::new(4, 4, 8, 4, fast::deflate_fast),     // 2 /* max speed, no lazy matches */
        Config::new(4, 6, 16, 6, medium::deflate_medium), // 3
        Config::new(4, 12, 32, 24, medium::deflate_medium), // 4 /* lazy matches */
        Config::new(8, 16, 32, 32, medium::deflate_medium), // 5
        Config::new(8, 16, 128, 128, medium::deflate_medium), // 6
        Config::new(8, 32, 128, 256, slow::deflate_slow), // 7
        Config::new(32, 128, 258, 1024, slow::deflate_slow), // 8
        Config::new(32, 258, 258, 4096, slow::deflate_slow), // 9 /* max compression */
    ]
};
