//! `Polygon` type.

use super::pnts;
use crate::{durandal::{bin::OptU16, err::*, fixed::Unit},
            marathon::xfer::TransferMode};
use bitflags::bitflags;
use std::convert::TryFrom;

/// 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 = PolyType::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 = PolyType::new_old(ptype, pdata)?;

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

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

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

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

/// A polygon segment.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Default, Eq, PartialEq)]
pub struct Polygon
{
   pub ptype:   PolyType,
   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(Debug, Eq, PartialEq)]
pub enum PolyType
{
   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,
}

bitflags! {
   /// Flags for `Polygon`.
   #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
   pub struct PolyFlags: u16
   {
      const DETACHED = 1 << 14;
   }
}

// EOF
