//! Macintosh archived format header utilities.

use crate::file::SeekBackToStart;
use std::io::{SeekFrom, prelude::*};

/// Skips over an Apple Single header. Returns true if one was found.
pub fn skip_apple_single<R>(fp: &mut R) -> bool
	where R: Read + Seek
{
	let mut fp = SeekBackToStart::new(fp);

	let mut magic = [0; 8];
	let     magic = if fp.read(&mut magic).is_ok() {magic} else {return false;};

	// check magic numbers
	if magic != [0, 5, 22, 0, 0, 2, 0, 0] {
		return false;
	}

	let mut num = [0; 2];
	let     num = if fp.read(&mut num).is_ok() {num} else {return false;};
	let     num = u64::from(u16::from_be_bytes(num));

	if fp.seek(SeekFrom::Start(26 + 12 * num)).is_err() |
	   fp.seek(SeekFrom::Start(26)).is_err() {
		return false;
	}

	// get the resource fork (entity 1)
	for _ in 0..num {
		let mut ent = [0; 4];
		let mut ofs = [0; 4];
		let mut len = [0; 4];
		let     ent = if fp.read(&mut ent).is_ok() {ent} else {return false;};
		let     ofs = if fp.read(&mut ofs).is_ok() {ofs} else {return false;};
		let     len = if fp.read(&mut len).is_ok() {len} else {return false;};
		let     ent = u32::from_be_bytes(ent);
		let     ofs = u64::from(u32::from_be_bytes(ofs));
		let     len = u64::from(u32::from_be_bytes(len));

		if ent == 1 {
			if fp.seek(SeekFrom::Start(ofs + len)).is_ok() &
			   fp.seek(SeekFrom::Start(ofs)).is_ok() {
				fp.set_seek(false);
				return true;
			} else {
				return false;
			}
		}
	}

	// no resource fork
	false
}

/// Skips over a Mac Binary II header. Returns true if one was found.
pub fn skip_macbin<R>(fp: &mut R) -> bool
where R: Read + Seek
{
	let mut fp = SeekBackToStart::new(fp);

	let mut head = [0; 128];
	let     head = if fp.read(&mut head).is_ok() {head} else {return false;};

	// check legacy version, length, zero fill, and macbin2 version. I swear,
	// this isn't *completely* magic
	if head[0] != 0 || head[1] > 63 || head[74] != 0 || head[123] > 129 {
		return false;
	}

	let mut crc = 0;

	// check header crc
	for &byte in head.iter().take(124) {
		for j in 8..16 {
			let d = u16::from(byte) << j;

			if (d ^ crc) & 0x8000 == 0 {
				crc <<= 1;
			} else {
				crc = crc << 1 ^ 0x1021;
			}
		}
	}

	// if ok, resource fork follows
	if crc == u16::from_be_bytes([head[124], head[125]]) {
		fp.set_seek(false);
		true
	} else {
		false
	}
}

/// Reads a `MacBin` or `AppleSingle` header if there is one and skips past it
/// from the start of the header to the resource fork (if one is found.) Returns
/// true if either one was found.
pub fn skip_mac_header<R>(fp: &mut R) -> bool
where R: Read + Seek
{
	if skip_macbin(fp) {
		return true;
	}

	let _ = fp.seek(SeekFrom::Start(0));

	if skip_apple_single(fp) {
		return true;
	}

	let _ = fp.seek(SeekFrom::Start(0));

	false
}

// EOF
