pub use self::report::{Report};
use self::assessment::{Assessment};

mod assessment;
mod bias;
mod blinking;
mod frc;
mod report;
mod sampling;
mod squirrel;

use crate::{constants, io};
use crate::settings::{Settings, ReportSettings};
use crate::results::Results;

use std::path::{Path};

fn error_f(name: &str) -> String
{
	format!("Data for {name} split is missing")
}

fn generate_localisation_precision_assessment<T>(results: &Results<T>) -> Result<Assessment, String>
{
	let mean_precision = results.mean_precision().ok_or_else(|| format!("Mean precision not calculated"))?;
	let mut assessment = Assessment::pass_with("Localisation", "Mean precision of localisations.");
	assessment.set_score(2.0 * mean_precision);
	Ok(assessment)
}

fn generate_blinking_assessment<T>(results: &Results<T>, settings: &ReportSettings) -> Result<Assessment, String>
{
	let drift = results.drift_split_frc().ok_or_else(|| error_f(constants::DRIFT_SPLIT))?;
	let zip = results.zip_split_frc().ok_or_else(|| error_f(constants::ZIP_SPLIT))?;
	blinking::assess(settings.blinking(), drift, zip)
}

fn generate_sampling_report<T, P: AsRef<Path>>(results: &Results<T>, settings: &Settings, output_directory: P) -> Result<Assessment, String>
{
	let drift = results.drift_split_frc().ok_or_else(|| error_f(constants::DRIFT_SPLIT))?;
	sampling::assess(drift, settings, output_directory)
}

fn generate_drift_assessment<T>(results: &Results<T>, settings: &ReportSettings) -> Result<Assessment, String>
{
	let drift = results.drift_split_frc().ok_or_else(|| error_f(constants::DRIFT_SPLIT))?;
	let half = results.half_split_frc().ok_or_else(|| error_f(constants::HALF_SPLIT))?;
	frc::assess_drift(drift, half, settings.drift_difference_within())
}

fn generate_frc_resolution_assessment<T>(results: &Results<T>, settings: &Settings) -> Result<Assessment, String>
{
	let drift = results.drift_split_frc().ok_or_else(|| error_f(constants::DRIFT_SPLIT))?;
	Ok(frc::assess_resolution(drift, settings.super_res_pixel_size_nm()))
}

fn generate_frc_magnification_assessment<T>(results: &Results<T>, settings: &ReportSettings) -> Result<Assessment, String>
{
	let drift = results.drift_split_frc().ok_or_else(|| error_f(constants::DRIFT_SPLIT))?;
	let mag_settings = settings.magnification();
	let lower = mag_settings.lower();
	let upper = mag_settings.upper();
	Ok(frc::assess_magnification(drift, lower, upper))
}

fn generate_bias_assessment<T: AsRef<Path>>(settings: &Settings, results: &Results<T>, report_settings: &ReportSettings) -> Result<Assessment, String>
{
	let hawkman = results.hawkman().ok_or_else(|| "HAWKMAN data is missing.")?;
	bias::assess(report_settings.bias(), settings, hawkman, results.mean_precision())
}

fn generate_squirrel_report<T>(settings: &ReportSettings, results: &Results<T>) -> Result<Assessment, String>
{
	let generated_squirrel = results.generated_wf_squirrel();
	let true_squirrel = results.true_wf_squirrel();
	squirrel::assess(settings.squirrel(), generated_squirrel, true_squirrel)
}

pub fn assess_results<T: AsRef<Path>>(settings: &Settings, results: &Results<T>) -> Report
{
	let report_settings = settings.report_settings();
	let output_directory = settings.output_directory();
	
	let mut report = Report::new();
	let mut errors = Vec::new();

	let report_location = io::report_location(&output_directory);
	println!("writing report to: {}", report_location.display());
	match std::fs::create_dir_all(&report_location)
	{
		Ok(_) => {},
		Err(e) => 
		{
			println!("Error creating report directory {}: {e}", report_location.display());
			return report
		}
	}

	let localisation_precision = generate_localisation_precision_assessment(results);
	if localisation_precision.is_ok()
	{
		report.add_localisation_precision_assessment(localisation_precision.unwrap());
	}
	else 
	{
		errors.push(localisation_precision.unwrap_err());
	}
	
	let drift = generate_drift_assessment(results, report_settings);
	if drift.is_ok()
	{
		report.add_drift_assessment(drift.unwrap());
	}
	else 
	{
		errors.push(drift.unwrap_err());
	}

	let frc_resolution = generate_frc_resolution_assessment(results, settings);
	if frc_resolution.is_ok()
	{
		report.add_frc_resolution_assessment(frc_resolution.unwrap());
	}
	else 
	{
		errors.push(frc_resolution.unwrap_err());
	}

	let blinking = generate_blinking_assessment(results, report_settings);
	if blinking.is_ok()
	{
		report.add_blinking_assessment(blinking.unwrap())
	}
	else 
	{
		errors.push(blinking.unwrap_err());
	}

	let sampling = generate_sampling_report(results, settings, &report_location);
	if sampling.is_ok()
	{
		report.add_frc_sampling_assessment(sampling.unwrap())
	}
	else 
	{
		errors.push(sampling.unwrap_err());
	}

	let magnification = generate_frc_magnification_assessment(results, report_settings);
	if magnification.is_ok()
	{
		report.add_magnification_assessment(magnification.unwrap())
	}
	else 
	{
		errors.push(magnification.unwrap_err());
	}

	let bias = generate_bias_assessment(settings, results, report_settings);
	if bias.is_ok()
	{
		report.add_bias_assessment(bias.unwrap())
	}
	else 
	{
		errors.push(bias.unwrap_err())
	}

	let squirrel = generate_squirrel_report(report_settings, results);
	if squirrel.is_ok()
	{
		report.add_squirrel_assessment(squirrel.unwrap());
	}
	else 
	{
		errors.push(squirrel.unwrap_err())	
	}

	report.fidge_precision_assessments();
	match report.write_to_directory(&report_location)
	{
		Ok(_) => {},
		Err(e) => 
		{
			println!("Could not write report to {}: {}", report_location.display(), e.to_string())
		}
	}

	let calibration_curves = frc::write_calibration_curves_to(&report_location);
	if calibration_curves.is_err()
	{
		errors.push(calibration_curves.unwrap_err().to_string());
	}

	for error in errors
	{
		println!("{error}");
	}
	
	report
}

#[cfg(test)]
mod tests 
{
	use super::*;

	#[test]
	fn assessment_writing()
	{
		assert_eq!(Assessment::pass_with("some", "thing").to_string(), "some: passed\nthing");
		assert_eq!(Assessment::fail_with("some", "thing").to_string(), "some: failed\nthing");
	}

	#[test]
	fn write_report()
	{
		let mut report = Report::default();
		report.add_frc_sampling_assessment(Assessment::pass_with("some", "thing"));
		report.add_frc_resolution_assessment(Assessment::fail_with("something", "else"));
		assert_eq!(report.to_string(), "something: failed\nelse\nsome: passed\nthing\n");
	}

}