A rust port of vgm2mid by Paul Jensen and Valley Bell
use crate::midi_shim::MIDIShim;
use crate::sn76489::{SN76489, SN76489_TONE_1, SN76489_TONE_2, SN76489_TONE_3};
use crate::ym2612::YM2612;
use crate::config::Config;
use anyhow::{bail, Result};

// GYM Command Constants
const DELAY_166MS: u8 = 0;
const YM2612_PORT0: u8 = 1;
const YM2612_PORT1: u8 = 2;
const YM2612_PSG: u8 = 3;

struct GYMHeader {
	str_gymx: [u8; 4],
}

fn parse_gym_file_header(file_data: &[u8]) -> Result<Option<GYMHeader>> {
	if file_data.len() < 428 {
		return Ok(None);
	}

	let mut header = GYMHeader { str_gymx: [0; 4] };

	if &file_data[0..4] != "GYMX".as_bytes() {
		return Ok(None);
	} else {
		header.str_gymx.clone_from_slice(&file_data[0..4]);
	}

	Ok(Some(header))
}

pub(crate) fn convert_gym_to_mid(
	file_data: &[u8],
	config: &Config,
	midi: &mut MIDIShim,
) -> Result<()> {
	let _file_header = parse_gym_file_header(file_data)?;

	let mut file_pos = 0_usize;
	let register = 0_u8;

	let mut _conversion_status_current = 0;
	let _conversion_status_total = file_data.len();

	let _mid_trackdata: Vec<u8> = Vec::new();
	let _mid_file_pos = 0;

	let clock_sn76489 = 3579545;
	let clock_ym2612 = 3579545;
	let fsam_2612 = clock_ym2612 as f64 / 72.0;

	let mut ym2612 = YM2612::new(config, None);
	let mut sn76489 = SN76489::new(false, clock_sn76489, config, None);

	let t6w28_sn76489 = false;

	midi.data_init(true, t6w28_sn76489);

	//DAC_Pos = 0
	//ReDim DAC_Data(0)
	//let DelayTime: i64
	//let Ttime: f64;
	//let Ttime: f64 = 0;

	loop {
		let switch = file_data[file_pos];
		match switch {
			DELAY_166MS => {
				process_delay(midi, &mut sn76489, &mut file_pos);
			},
			YM2612_PORT0 | YM2612_PORT1 => {
				process_ym2612(
					switch,
					file_data,
					&mut file_pos,
					&mut ym2612,
					register,
					fsam_2612,
					midi,
				)?;
			},
			YM2612_PSG => {
				process_sn76489(file_data, &mut file_pos, &mut sn76489, midi)?;
			},
			_ => file_pos += 1,
		}

		_conversion_status_current = file_pos;

		if file_pos >= file_data.len() {
			break;
		}
	}
	Ok(())
}

fn process_delay(midi: &mut MIDIShim, sn76489: &mut SN76489, file_pos: &mut usize) {
	midi.delta_time += 735;
	for channel in 0x0..=0x2 {
		sn76489.state.note_delay[channel] += 735;
	}
	*file_pos += 1;
}

fn process_ym2612(
	switch: u8,
	file_data: &[u8],
	file_pos: &mut usize,
	ym2612: &mut YM2612,
	register: u8,
	fsam_2612: f64,
	midi: &mut MIDIShim,
) -> Result<()> {
	let port = match switch {
		YM2612_PORT0 => 0,
		YM2612_PORT1 => 3,
		_ => bail!("Pattern matched to a number other YM2612_PORT0 or YM2612_PORT1. This should never occur.")
	};

	let data = file_data[*file_pos + 2];
	//if bytRegister != YM2612_DAC { _
	//
	ym2612.command_handle(port, register, data, fsam_2612, midi)?;
	*file_pos += 3;
	//if bytRegister = YM2612_DAC {
	//	ReDim Preserve DAC_Data(DAC_Pos)
	//	DAC_Data(DAC_Pos) = bytData
	//	DAC_Pos = DAC_Pos + 1
	//	DelayTime = 0
	//}
	Ok(())
}

fn process_sn76489(
	file_data: &[u8],
	file_pos: &mut usize,
	sn76489: &mut SN76489,
	midi: &mut MIDIShim,
) -> Result<()> {
	let sn76489_msb = file_data[*file_pos + 1];
	let mut sn76489_lsb = 0;
	match sn76489_msb & 240 {
		SN76489_TONE_1 | SN76489_TONE_2 | SN76489_TONE_3 => {
			sn76489_lsb = file_data[*file_pos + 3];
			*file_pos += 4;
		},
		_ => *file_pos += 2,
	}
	sn76489.command_handle(sn76489_msb, 0, midi)?;
	sn76489.command_handle(sn76489_lsb, 0, midi)?;
	Ok(())
}