use image_similarity::store::DescriptorStore;
use image_similarity::descriptors::get_all_descriptors;
use image_similarity::mutators::get_all_mutators;
use log::{error};
use std::path::PathBuf;
use std::{fs};
use clap::Parser;
use plotters::prelude::*;

#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cfg {
    /// Path that contains the input images. Will not traverse directories.
    path: PathBuf,
}

fn main() {
    env_logger::init();
    let cfg = Cfg::parse();
	let mutators = get_all_mutators();
	//let descriptors = get_all_descriptors();
    let mut stores: Vec<DescriptorStore> = Vec::new();

    for descriptor in get_all_descriptors() {
        let filename = format!("{}.store", descriptor.info());
        let store = DescriptorStore::new(descriptor).with_file(filename);
        stores.push(store);
    }
    
    for node in fs::read_dir(cfg.path).unwrap() {
        let file = node.expect("Error walking directory");
        let name = match file.file_name().into_string() {
            Ok(v) => v,
            Err(e) => {
                error!("Error reading {}: {:?}:", file.path().to_string_lossy(), e);
                continue
            }
        };

        let mut get = true;
        for store in &stores {
            if store.has_value(&name) {
                get = false;
                break;
            }
        }

        // We assume that if a base image exists in the store, the mutated images also exist
        if get {
            let img = match image::open(file.path()) {
                Ok(v) => v,
                Err(e) => {
                    error!("Failed to process {}: {}", name, e);
                    continue
                }
            }.thumbnail_exact(8, 8);

            //Store the phashes of the base image
            for store in &mut stores {
                store.store(&img, name.clone());
            }

            for mutator in &mutators {
                let mutated = mutator.mutate(&img);
                let mutated_name = "mut".to_string() + &mutator.tag() + &name.clone();
                for store in &mut stores {
                    store.store(&mutated, mutated_name.clone());
                }
            }
        }
    }
    for store in &stores {
        store.save().expect("Error saving store");
        println!("{store}");
    }

    // Create PR-curve graphs from stores
    let max_threshold = 8;
    for store in &stores {
        println!("Stats for {}", store.descriptor.info());
        let stats = store.get_stats(max_threshold);
        let filename = format!("{}-pr.png", store.descriptor.info());
        let root = BitMapBackend::new(&filename, (1024, 768)).into_drawing_area();
        root.fill(&WHITE).unwrap();
        let mut chart = ChartBuilder::on(&root)
            // Set the caption of the chart
            .caption("PR curves", ("sans-serif", 40).into_font())
            .margin(25)
            .set_all_label_area_size(50)
            // Finally attach a coordinate on the drawing area and make a chart context
            .build_cartesian_2d(0f64..1f64, 0f64..1f64).unwrap();
        chart.configure_mesh()
            .x_labels(10)
            .y_labels(10)
            .disable_mesh()
            .x_desc("Recall")
            .y_desc("Precision")
            .x_label_formatter(&|x| format!("{x:.3}"))
            .y_label_formatter(&|x| format!("{x:.3}"))
            .draw().unwrap();
        let colors = [&RED, &BLUE, &CYAN, &MAGENTA, &BLACK, &GREEN, &YELLOW];
        let n = colors.len();
        for (i, mutator_stats) in stats.into_iter().enumerate() {
            // And we can draw something in the drawing area
            let pr_curve = mutator_stats.pr_curve(max_threshold);
            println!("{}:", mutator_stats.name);
            for (x, y) in pr_curve.clone() {
                println!("{x}, {y}");
            }
            let color = colors[i % n];
            chart.draw_series(LineSeries::new(
                pr_curve,
                color.filled(),
            ).point_size(2)).unwrap()
                .label(mutator_stats.name)
                .legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], color));
        }
        chart.configure_series_labels()
            .position(SeriesLabelPosition::MiddleLeft)
            .border_style(BLACK)
            .draw()
            .unwrap();
    }
}
