mod color_dist;
mod encode;
mod indexed;

use anyhow::{bail, Result};
use clap::Parser;
use encode::{choose_encoding, Region};
use image::{DynamicImage, RgbImage};
use indexed::PicoImage;

#[derive(Debug, Parser)]
#[command()]
struct Args {
    #[arg(short, long, value_name = "FILE")]
    input: String,
    #[arg(short, long, value_name = "FILE")]
    output: String,

    #[arg(short = 'e', long, value_name = "NUM")]
    max_err: Option<f32>,
}

fn main() -> Result<()> {
    let args = Args::parse();
    let img = image::open(&args.input)?;
    let img = match img {
        DynamicImage::ImageRgb8(img) => img,
        _ => bail!("Unsupported type: {:?}", img),
    };
    println!("Opened {} ({}x{})", &args.input, img.width(), img.height());
    let mut output = RgbImage::new(128, 128);
    for x in 0..=127 {
        for y in 0..=127 {
            let pixel = img.get_pixel(x * img.width() / 128, y * img.height() / 128);
            output.put_pixel(x, y, *pixel);
        }
    }

    // Lossy compression
    let error_scale_factor = 0.2; // Adjusted by feel. Q=0 is unrecognizable but not blank.
    let error_per_pixel = (1.0 - args.max_err.unwrap_or(80.0) / 100.0) * error_scale_factor;
    let sq_error_per_pixel = error_per_pixel * error_per_pixel;
    quadtree_quant(&mut output, sq_error_per_pixel * 128.0 * 128.0);

    // Convert to indexed
    let indexed = PicoImage::to_indexed(&output);
    output = indexed.to_rgb();

    output.save(&args.output)?;
    println!("Wrote {}", &args.output);
    Ok(())
}

fn quadtree_quant(img: &mut RgbImage, max_err: f32) {
    let region = Region {
        x: 0,
        y: 0,
        width: img.width(),
        height: img.height(),
    };
    let tree = choose_encoding(img, region, max_err);
    println!(
        "error = {}, predicted size = {} bytes",
        tree.error, tree.bytes
    );
    tree.apply(img, region);
}
