//! `Monster` type.

use crate::durandal::{bin::OptU16,
                      err::*,
                      fixed::{Fixed, Unit}};
use bitflags::bitflags;

use super::{attk, damg};

/// Reads a `MNpx` chunk.
pub fn read(b: &[u8]) -> ResultS<(Monster, usize)>
{
   read_data! {
      endian: BIG, buf: b, size: 156, start: 0, data {
         let collection       = u16[0];
         let vitality         = u16[2];
         let dty_immune       = u32[4] flag damg::DamageTypeFlags;
         let dty_weak         = u32[8] flag damg::DamageTypeFlags;
         let flags            = u32[12] flag MonsterFlags;
         let cls_self         = u32[16] flag MonsterClass;
         let cls_friend       = u32[20];
         let cls_enemy        = u32[24];
         let snd_pitch        = Fixed[28];
         let snd_see_enemy    = OptU16[32];
         let snd_see_friend   = OptU16[34];
         let snd_seeclear     = OptU16[36];
         let snd_kill         = OptU16[38];
         let snd_apologize    = OptU16[40];
         let snd_amicide      = OptU16[42];
         let snd_flaming      = OptU16[44];
         let snd_active       = OptU16[46];
         let snd_active_mask  = u16[48];
         let typ_item         = OptU16[50];
         let radius           = Unit[52];
         let height           = Unit[54];
         let height_hover     = Unit[56];
         let ledge_min        = Unit[58];
         let ledge_max        = Unit[60];
         let ext_vel_scale    = Fixed[62];
         let fxt_impact       = OptU16[66];
         let fxt_impact_melee = OptU16[68];
         let fxt_trail        = OptU16[70];
         let half_fov_horz    = u16[72];
         let half_fov_vert    = u16[74];
         let view_range       = Unit[76];
         let view_range_dark  = Unit[78];
         let intelligence     = u16[80];
         let speed            = u16[82];
         let gravity          = u16[84];
         let vel_terminal     = u16[86];
         let door_try_mask    = u16[88];
         let expl_radius      = OptU16[90];
         let expl_damage      = damg::read[92; 12];
         let seq_hit          = OptU16[104];
         let seq_dying_hard   = OptU16[106];
         let seq_dying_soft   = OptU16[108];
         let seq_dead_hard    = OptU16[110];
         let seq_dead_soft    = OptU16[112];
         let seq_standing     = u16[114];
         let seq_moving       = u16[116];
         let seq_tele_in      = OptU16[118];
         let seq_tele_out     = OptU16[120];
         let atk_frequency    = u16[122];
         let atk_melee        = attk::read[124; 16];
         let atk_range        = attk::read[140; 16];
      }
   }

   // friend and enemy fields MUST truncate because the original source code
   // used `-1` to represent "all classes" which should be invalid normally

   let cls_friend = MonsterClass::from_bits_truncate(cls_friend);
   let cls_enemy  = MonsterClass::from_bits_truncate(cls_enemy);

   Ok((Monster{collection, vitality, dty_immune, dty_weak, flags, cls_self,
               cls_friend, cls_enemy, snd_pitch, snd_see_enemy, snd_see_friend,
               snd_seeclear, snd_kill, snd_apologize, snd_amicide, snd_flaming,
               snd_active, snd_active_mask, typ_item, radius, height,
               height_hover, ledge_min, ledge_max, ext_vel_scale, fxt_impact,
               fxt_impact_melee, fxt_trail, half_fov_horz, half_fov_vert,
               view_range, view_range_dark, intelligence, speed, gravity,
               vel_terminal, door_try_mask, expl_radius, expl_damage, seq_hit,
               seq_dying_hard, seq_dying_soft, seq_dead_hard, seq_dead_soft,
               seq_standing, seq_moving, seq_tele_in, seq_tele_out,
               atk_frequency, atk_melee, atk_range}, 156))
}

/// A monster definition.
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
#[derive(Debug, Eq, PartialEq)]
pub struct Monster
{
   pub collection:       u16,
   pub vitality:         u16,
   pub dty_immune:       damg::DamageTypeFlags,
   pub dty_weak:         damg::DamageTypeFlags,
   pub flags:            MonsterFlags,
   pub cls_self:         MonsterClass,
   pub cls_friend:       MonsterClass,
   pub cls_enemy:        MonsterClass,
   pub snd_pitch:        Fixed,
   pub snd_see_enemy:    OptU16,
   pub snd_see_friend:   OptU16,
   pub snd_seeclear:     OptU16,
   pub snd_kill:         OptU16,
   pub snd_apologize:    OptU16,
   pub snd_amicide:      OptU16,
   pub snd_flaming:      OptU16,
   pub snd_active:       OptU16,
   pub snd_active_mask:  u16,
   pub typ_item:         OptU16,
   pub radius:           Unit,
   pub height:           Unit,
   pub height_hover:     Unit,
   pub ledge_min:        Unit,
   pub ledge_max:        Unit,
   pub ext_vel_scale:    Fixed,
   pub fxt_impact:       OptU16,
   pub fxt_impact_melee: OptU16,
   pub fxt_trail:        OptU16,
   pub half_fov_horz:    u16,
   pub half_fov_vert:    u16,
   pub view_range:       Unit,
   pub view_range_dark:  Unit,
   pub intelligence:     u16,
   pub speed:            u16,
   pub gravity:          u16,
   pub vel_terminal:     u16,
   pub door_try_mask:    u16,
   pub expl_radius:      OptU16,
   pub expl_damage:      damg::Damage,
   pub seq_hit:          OptU16,
   pub seq_dying_hard:   OptU16,
   pub seq_dying_soft:   OptU16,
   pub seq_dead_hard:    OptU16,
   pub seq_dead_soft:    OptU16,
   pub seq_standing:     u16,
   pub seq_moving:       u16,
   pub seq_tele_in:      OptU16,
   pub seq_tele_out:     OptU16,
   pub atk_frequency:    u16,
   pub atk_melee:        attk::Attack,
   pub atk_range:        attk::Attack,
}

bitflags! {
   /// Flags for a monster.
   #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
   pub struct MonsterFlags: u32
   {
      const IGNORE_LOS         = 1;
      const FLYING             = 1 << 1;
      const ALIEN              = 1 << 2;
      const MAJOR              = 1 << 3;
      const MINOR              = 1 << 4;
      const NO_OMIT            = 1 << 5;
      const FLOATS             = 1 << 6;
      const NO_ATTACK          = 1 << 7;
      const SNIPE              = 1 << 8;
      const INVISIBLE          = 1 << 9;
      const SUBTLY_INVISIBLE   = 1 << 10;
      const KAMIKAZE           = 1 << 11;
      const BERSERKER          = 1 << 12;
      const ENLARGED           = 1 << 13;
      const DELAYED_DEATH      = 1 << 14;
      const FIRE_SYMMETRICAL   = 1 << 15;
      const NUCLEAR_DEATH      = 1 << 16;
      const NO_FIRE_BACKWARDS  = 1 << 17;
      const CAN_DIE_IN_FLAMES  = 1 << 18;
      const WAIT_FOR_GOOD_SHOT = 1 << 19;
      const TINY               = 1 << 20;
      const FAST_ATTACK        = 1 << 21;
      const LIKES_WATER        = 1 << 22;
      const LIKES_SEWAGE       = 1 << 23;
      const LIKES_LAVA         = 1 << 24;
      const LIKES_GOO          = 1 << 25;
      const TELE_UNDER_MEDIA   = 1 << 26;
      const USE_RANDOM_WEAPON  = 1 << 27;
   }
}

bitflags! {
   /// The composite type of a monster.
   #[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
   pub struct MonsterClass: u32
   {
      const PLAYER           = 1;
      const CIVILIAN         = 1 << 1;
      const MADD             = 1 << 2;
      const POSSESSED_HUMMER = 1 << 3;
      const DEFENDER         = 1 << 4;
      const FIGHTER          = 1 << 5;
      const TROOPER          = 1 << 6;
      const HUNTER           = 1 << 7;
      const ENFORCER         = 1 << 8;
      const JUGGERNAUT       = 1 << 9;
      const HUMMER           = 1 << 10;
      const COMPILER         = 1 << 11;
      const CYBORG           = 1 << 12;
      const ASSIMILATED      = 1 << 13;
      const TICK             = 1 << 14;
      const YETI             = 1 << 15;
   }
}

// EOF
