//! `Polygon` type.

use super::pnts;
use crate::{bin::OptU16, err::*, fixed::Unit, xfer::TransferMode};

/// Reads a polygon for either M1 or M2.
pub fn read_poly_inter(b: &[u8]) -> ResultS<Polygon>
{
	read_data! {
		endian: BIG, buf: b, size: 128, start: 0, data {
			let tex_flr = OptU16[40];
			let tex_cei = OptU16[42];
			let hei_flr = Unit[44];
			let hei_cei = Unit[46];
			let lit_flr = u16[48];
			let lit_cei = u16[50];
			let xfr_flr = u16[64] enum TransferMode;
			let xfr_cei = u16[66] enum TransferMode;
		}
	}

	Ok(Polygon{tex_flr, tex_cei, hei_flr, hei_cei, lit_flr, lit_cei, xfr_flr,
	           xfr_cei, ..Polygon::default()})
}

/// Reads a `POLY` chunk.
pub fn read(b: &[u8]) -> ResultS<(Polygon, usize)>
{
	read_data! {
		endian: BIG, buf: b, size: 128, start: 0, data {
			let ptype   = u16[0];
			let pdata   = u16[4];
			let ori_flr = pnts::read_o[108; 4];
			let ori_cei = pnts::read_o[112; 4];
			let med_ind = OptU16[116];
			let med_ctl = u16[118];
			let snd_amb = OptU16[122];
			let snd_ind = u16[120];
			let snd_rnd = OptU16[124];
		}
	}

	let poly  = read_poly_inter(b)?;
	let ptype = PolygonType::new(ptype, pdata)?;

	Ok((Polygon{ptype, ori_flr, ori_cei, med_ind, med_ctl, snd_ind, snd_amb,
	            snd_rnd, ..poly}, 128))
}

/// Reads an old `POLY` chunk.
pub fn read_old(b: &[u8]) -> ResultS<(Polygon, usize)>
{
	read_data! {
		endian: BIG, buf: b, size: 128, start: 0, data {
			let ptype = u16[0];
			let pdata = u16[4];
		}
	}

	let poly  = read_poly_inter(b)?;
	let ptype = PolygonType::new_old(ptype, pdata)?;

	Ok((Polygon{ptype, ..poly}, 128))
}

impl PolygonType
{
	/// Creates a `PolygonType` from a `n`/`pdata` pair.
	pub fn new(n: u16, pdata: u16) -> Result<Self, ReprError>
	{
		match n {
			0  => Ok(PolygonType::Normal),
			1  => Ok(PolygonType::ImpassItem),
			2  => Ok(PolygonType::ImpassMons),
			3  => Ok(PolygonType::Hill),
			4  => Ok(PolygonType::Base),
			5  => Ok(PolygonType::Platform(pdata)),
			6  => Ok(PolygonType::TrigLightOn(pdata)),
			7  => Ok(PolygonType::TrigPlatOn(pdata)),
			8  => Ok(PolygonType::TrigLightOff(pdata)),
			9  => Ok(PolygonType::TrigPlatOff(pdata)),
			10 => Ok(PolygonType::Teleporter(pdata)),
			11 => Ok(PolygonType::ZoneBorder),
			12 => Ok(PolygonType::Goal),
			13 => Ok(PolygonType::TrigMonsVis),
			14 => Ok(PolygonType::TrigMonsInv),
			15 => Ok(PolygonType::TrigMonsDual),
			16 => Ok(PolygonType::TrigItems),
			17 => Ok(PolygonType::MustExplore),
			18 => Ok(PolygonType::AutoExit),
			19 => Ok(PolygonType::OuchMinor),
			20 => Ok(PolygonType::OuchMajor),
			21 => Ok(PolygonType::Glue),
			22 => Ok(PolygonType::GlueTrigger(pdata)),
			23 => Ok(PolygonType::GlueSuper),
			n  => Err(ReprError::new("PolygonType", n)),
		}
	}

	/// Creates a `PolygonType` from a Marathon 1 compatible `n`/`pdata` pair.
	fn new_old(n: u16, pdata: u16) -> Result<Self, ReprError>
	{
		match n {
			0  => Ok(PolygonType::Normal),
			1  => Ok(PolygonType::ImpassItem),
			2  => Ok(PolygonType::ImpassMons),
			3  => Ok(PolygonType::OuchMinor),
			4  => Ok(PolygonType::OuchMajor),
			5  => Ok(PolygonType::Platform(pdata)),
			6  => Ok(PolygonType::TrigLightOn(pdata)),
			7  => Ok(PolygonType::TrigPlatOn(pdata)),
			8  => Ok(PolygonType::TrigLightOff(pdata)),
			9  => Ok(PolygonType::TrigPlatOff(pdata)),
			10 => Ok(PolygonType::Teleporter(pdata)),
			11 => Ok(PolygonType::Glue),
			12 => Ok(PolygonType::GlueTrigger(pdata)),
			13 => Ok(PolygonType::GlueSuper),
			14 => Ok(PolygonType::MustExplore),
			15 => Ok(PolygonType::AutoExit),
			n  => Err(ReprError::new("PolygonType", n)),
		}
	}
}

impl Default for PolygonType
{
	fn default() -> Self {PolygonType::Normal}
}

/// A polygon segment.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Polygon {
	pub ptype:   PolygonType,
	pub tex_flr: OptU16,
	pub tex_cei: OptU16,
	pub hei_flr: Unit,
	pub hei_cei: Unit,
	pub lit_flr: u16,
	pub lit_cei: u16,
	pub xfr_flr: TransferMode,
	pub xfr_cei: TransferMode,
	pub ori_flr: pnts::Point,
	pub ori_cei: pnts::Point,
	pub med_ind: OptU16,
	pub med_ctl: u16,
	pub snd_ind: u16,
	pub snd_amb: OptU16,
	pub snd_rnd: OptU16,
}

/// The action type of a `Polygon`.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PolygonType {
	Normal,
	ImpassItem,
	ImpassMons,
	Hill,
	Base,
	Platform(u16),
	TrigLightOn(u16),
	TrigPlatOn(u16),
	TrigLightOff(u16),
	TrigPlatOff(u16),
	Teleporter(u16),
	ZoneBorder,
	Goal,
	TrigMonsVis,
	TrigMonsInv,
	TrigMonsDual,
	TrigItems,
	MustExplore,
	AutoExit,
	OuchMinor,
	OuchMajor,
	Glue,
	GlueTrigger(u16),
	GlueSuper,
}

c_bitfield! {
	/// Flags for `Polygon`.
	#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
	pub struct PolygonFlags: u16 {
		DETACHED = 14,
	}
}

// EOF
