A rust port of vgm2mid by Paul Jensen and Valley Bell
use crate::midi_shim::{
	MIDI_PATCH_ACOUSTIC_BASS, MIDI_PATCH_ACOUSTIC_GRAND_PIANO,
	MIDI_PATCH_ACOUSTIC_GUITAR_NYLON, MIDI_PATCH_CLARINET, MIDI_PATCH_ELECTRIC_GUITAR_JAZZ,
	MIDI_PATCH_FLUTE, MIDI_PATCH_FRENCH_HORN, MIDI_PATCH_HAMMOND_ORGAN, MIDI_PATCH_HARPSICHORD,
	MIDI_PATCH_OBOE, MIDI_PATCH_OCARINA, MIDI_PATCH_PAD_3_POLYSYNTH, MIDI_PATCH_SYNTH_BASS_2,
	MIDI_PATCH_SYNTH_STRINGS_1, MIDI_PATCH_TRUMPET, MIDI_PATCH_VIBRAPHONE,
};
use crate::vgm2mid::{PITCHWHEEL_SENSITIVITY_DEFAULT, PITCHWHEEL_STEPS_DEFAULT};
use crate::{strict, verbose};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};

#[derive(Serialize, Deserialize)]
pub struct Config {
	pub strict: bool,
	pub verbose: bool,

	pub dualchips: bool,
	pub pitchwheel_sensitivity: u8,
	pub pitchwheel_steps: u16,
	pub cnv_accuracy: u16,
	pub vgm_loops: u16,

	pub tempo_mod: bool,
	pub tempo_bpm: f64,
	pub tempo_mult: f64,
	pub tempo_div: f64,
	// Options
	pub sn76489_ch_disabled: [bool; 4],
	pub sn76489_noise_disabled: bool,
	pub sn76489_vol_disabled: [bool; 4],
	pub sn76489_voldep_notes: u8,

	pub ym2413_ch_disabled: [bool; 9],
	pub ym2413_vol_disabled: [bool; 9],
	pub ym2413_prog_disabled: [bool; 9],
	pub ym2413_percussion_disabled: bool,
	pub ym2413_optimized_vgms: bool,
	pub ym2413_midi_patch: [u8; 16],

	pub ym2612_ch_disabled: [bool; 6],
	pub ym2612_vol_disabled: [bool; 6],
	pub ym2612_prog_disabled: [bool; 6],
	pub ym2612_pan_disabled: [bool; 6],
	pub ym2612_midi_patch: [u8; 6],
	pub ym2612_dac_disabled: bool,

	pub ym2151_ch_disabled: [bool; 8],
	pub ym2151_vol_disabled: [bool; 8],
	pub ym2151_prog_disabled: [bool; 8],
	pub ym2151_pan_disabled: [bool; 8],
}

impl Default for Config {
	fn default() -> Self {
		Config {
			strict: true,
			verbose: false,

			dualchips: false,
			pitchwheel_sensitivity: PITCHWHEEL_SENSITIVITY_DEFAULT.into(),
			pitchwheel_steps: PITCHWHEEL_STEPS_DEFAULT.into(),
			cnv_accuracy: 480,
			vgm_loops: 0,

			tempo_mod: false,
			tempo_bpm: 120.0,
			tempo_mult: 256.0,
			tempo_div: 256.0,

			sn76489_ch_disabled: [false; 4],
			sn76489_noise_disabled: false,
			sn76489_vol_disabled: [false; 4],
			sn76489_voldep_notes: 2,

			ym2413_ch_disabled: [false; 9],
			ym2413_vol_disabled: [false; 9],
			ym2413_prog_disabled: [false; 9],
			ym2413_percussion_disabled: false,
			ym2413_optimized_vgms: false,
			ym2413_midi_patch: [
				MIDI_PATCH_OCARINA.into(),
				MIDI_PATCH_SYNTH_STRINGS_1.into(),
				MIDI_PATCH_ACOUSTIC_GUITAR_NYLON.into(),
				MIDI_PATCH_ACOUSTIC_GRAND_PIANO.into(),
				MIDI_PATCH_FLUTE.into(),
				MIDI_PATCH_CLARINET.into(),
				MIDI_PATCH_OBOE.into(),
				MIDI_PATCH_TRUMPET.into(),
				MIDI_PATCH_HAMMOND_ORGAN.into(),
				MIDI_PATCH_FRENCH_HORN.into(),
				MIDI_PATCH_PAD_3_POLYSYNTH.into(),
				MIDI_PATCH_HARPSICHORD.into(),
				MIDI_PATCH_VIBRAPHONE.into(),
				MIDI_PATCH_SYNTH_BASS_2.into(),
				MIDI_PATCH_ACOUSTIC_BASS.into(),
				MIDI_PATCH_ELECTRIC_GUITAR_JAZZ.into(),
			],

			ym2612_ch_disabled: [false; 6],
			ym2612_vol_disabled: [false; 6],
			ym2612_prog_disabled: [false; 6],
			ym2612_pan_disabled: [false; 6],
			ym2612_dac_disabled: false,
			ym2612_midi_patch: [0, 0, 0, 0, 0, 0],

			ym2151_ch_disabled: [false; 8],
			ym2151_vol_disabled: [false; 8],
			ym2151_prog_disabled: [false; 8],
			ym2151_pan_disabled: [false; 8],
		}
	}
}

impl Config {
	pub(crate) fn get_tempo_modifier(&self) -> f64 {
		if self.tempo_mod {
			//this was originally a comparison of TEMPO_MOD and 0x0 or 0x1.
			self.tempo_mult / self.tempo_div
		} else {
			120.0 / self.tempo_bpm
		}
	}
}

pub fn load_config_or_default(file_path: Option<&Path>) -> Result<Config> {
	let config_path = file_path.map_or_else(
		try_get_default_config_path,
		|path| Ok(Some(path.to_path_buf())),
	)?;

	config_path.map_or_else(use_default_config, fun_name1)
}

fn fun_name1(path: PathBuf) -> Result<Config> {
	match path.try_exists() {
		Ok(true) => {
			verbose!("Found configuration file at {}", path.display());
			fun_name(path)
		},
		Ok(false) => {
			strict!("Could not find configuration file at {}", path.display());
			use_default_config()
		},
		Err(error) => {
			strict!("{}", error);
			use_default_config()
		},
	}
}

fn fun_name(path: PathBuf) -> Result<Config> {
	std::fs::read_to_string(path).map_or_else(
		|err| {
			strict!("{}", err);
			use_default_config()
		},
		|cfg_string| {
			toml::from_str(&cfg_string).or_else(|err| {
				strict!("{}", err);
				use_default_config()
			})
		},
	)
}

fn use_default_config() -> Result<Config> {
	verbose!("Using default configuration.");
	Ok(Config::default())
}

fn try_get_default_config_path() -> Result<Option<PathBuf>> {
	directories::ProjectDirs::from("net", "vgmrips", "vgm-rs").map_or_else(
		|| {
			strict!("Failed to load default config path.");
			Ok(None)
		},
		|dirs| Ok(Some(dirs.config_dir().to_path_buf())),
	)
}