#include "monsters"
#include "../weapons/projectile"

const string Q1_ZOMBIE_MODEL = "models/quake1/m_zombie.mdl";

const string Q1_ZOMBIE_IDLE  = "quake1/monsters/zombie/idle.wav";
const string Q1_ZOMBIE_IDLEC = "quake1/monsters/zombie/idlec.wav";
const string Q1_ZOMBIE_PAIN  = "quake1/monsters/zombie/pain.wav";
const string Q1_ZOMBIE_DEATH = "quake1/monsters/zombie/death.wav";
const string Q1_ZOMBIE_GIB   = "quake1/monsters/zombie/gib.wav";
const string Q1_ZOMBIE_SHOOT = "quake1/monsters/zombie/shoot.wav";
const string Q1_ZOMBIE_HIT   = "quake1/monsters/zombie/hit.wav";
const string Q1_ZOMBIE_MISS  = "quake1/monsters/zombie/miss.wav";

const int SF_SPAWN_CRUCIFIED = 1;

enum Q1_ZOMBIE_EVENTS {
  ZOMBIE_CRUCIFIED_IDLE_SOUND = 1,
  ZOMBIE_WALK_IDLE_SOUND = 2,
  ZOMBIE_RUN_IDLE_SOUND = 3,
  ZOMBIE_RIGHT_ATTACK = 4,
  ZOMBIE_LEFT_ATTACK = 5,
  ZOMBIE_FALL_SOUND = 6,
  ZOMBIE_TEMP_DEAD = 7
}

class monster_qzombie : ScriptBaseMonsterEntity, monster_qgeneric {
  bool m_fDown = false;
  bool m_fPain = false;

  void Spawn() {
    Precache();

    g_EntityFuncs.SetModel(self, Q1_ZOMBIE_MODEL);
    g_EntityFuncs.SetSize(self.pev, Vector(-16, -16, -24), Vector(16, 16, 40));
    self.m_bloodColor = BLOOD_COLOR_RED;

    self.pev.health = 60;
    self.pev.solid = SOLID_SLIDEBOX;
    self.pev.movetype = MOVETYPE_STEP;

    self.StartMonster();
    self.m_MonsterState = MONSTERSTATE_NONE;

    self.m_FormattedName = "Zombie";

    self.pev.pain_finished = 0.0;

    m_iGibHealth = -5;

    if (self.pev.SpawnFlagBitSet(SF_SPAWN_CRUCIFIED)) {
      self.pev.movetype	= MOVETYPE_NONE;
      self.pev.takedamage	= DAMAGE_NO;

      // static monster as furniture
      m_iAIState = STATE_IDLE;
      SetActivity(ACT_SLEEP);

      SetThink(ThinkFunction(MonsterThink));
      self.pev.nextthink = g_Engine.time + (Math.RandomFloat(1, 10) * 0.1);
    } else {
      WalkMonsterInit();
    }
  }

  void Precache() {
    g_Game.PrecacheModel(Q1_ZOMBIE_MODEL);
    g_Game.PrecacheModel("models/quake1/zombiegib.mdl");

    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_IDLE);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_IDLEC);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_HIT);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_MISS);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_DEATH);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_GIB);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_SHOOT);
    g_SoundSystem.PrecacheSound(Q1_ZOMBIE_PAIN);
  }

  void AI_Idle() {
    if (self.pev.SpawnFlagBitSet(SF_SPAWN_CRUCIFIED))
      return; // stay idle

    if (FindTarget())
      return;
    
    if (g_Engine.time > m_flPauseTime) {
      MonsterWalk();
      return;
    }
  }

  void MonsterIdle() {
    m_iAIState = STATE_IDLE;
    SetActivity(ACT_IDLE);
    m_flMonsterSpeed = 0;
  }

  void MonsterDefeated() {
    g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_IDLE, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));

    self.pev.health = 60;
    self.pev.solid = SOLID_SLIDEBOX;

    if (g_EngineFuncs.WalkMove(self.edict(), 0, 0, WALKMOVE_NORMAL) == 0) {
      // no space to standing up (e.g. player blocked)
      self.pev.solid = SOLID_NOT;
      self.pev.nextthink = g_Engine.time + 5.0f;
    } else {
      self.ResetSequenceInfo();
      SetThink(ThinkFunction(MonsterThink));
      self.pev.nextthink = g_Engine.time + 0.1f;
    }
  }

  void MonsterSight() {
    g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_IDLE, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));
  }

  void MonsterWalk() {
    m_iAIState = STATE_WALK;
    SetActivity(ACT_WALK);
    m_flMonsterSpeed = 1;
  }

  void MonsterRun() {
    m_iAIState = STATE_RUN;
    SetActivity(ACT_RUN);
    m_flMonsterSpeed = 4;
    self.pev.radsuit_finished = 0.0;
    m_fDown = false;
    m_fPain = false;
  }

  void ThrowMeat(int iAttachment, Vector vecOffset) {
    if (!m_hEnemy) return;
    CBaseEntity@ pEnemy = m_hEnemy;

    g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_WEAPON, Q1_ZOMBIE_SHOOT, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));
    q1_ZombieMissile(self, pEnemy, self.pev.origin, vecOffset);

    MonsterRun();
  }

  void MonsterPain(CBaseEntity@ pAttacker, float flDamage) {
    self.pev.health = 60; // allways reset health

    if (flDamage < 9)
      return; // totally ignore

    if (m_iAIState != STATE_PAIN)
      g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_PAIN, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 73 + Math.RandomLong(0, 0x1f));

    m_iAIState = STATE_PAIN;

    if (m_fDown)
      return; // down on ground, so don't reset any counters

    // go down immediately if a big enough hit
    if (flDamage >= 25) {
      SetActivity(ACT_BIG_FLINCH);
      m_fDown = true;
      AI_Pain(2);
      return;
    }
    
    if (m_fPain) {
      // if hit again in next gre seconds while not in pain frames, definately drop
      self.pev.pain_finished = g_Engine.time + 3;
      return; // currently going through an animation, don't change
    }
    
    if (self.pev.pain_finished > g_Engine.time) {
      // hit again, so drop down
      SetActivity(ACT_BIG_FLINCH);
      m_fDown = true;
      AI_Pain(2);
      return;
    }

    // go into one of the fast pain animations	
    m_fPain = true;

    SetActivity(ACT_SMALL_FLINCH);
    AI_PainForward(3);
  }

  void MonsterMissileAttack() {
    m_iAIState = STATE_ATTACK;
    SetActivity(ACT_MELEE_ATTACK1);
  }

  bool MonsterHasMissileAttack() { return true; }
  bool MonsterHasMeleeAttack() { return false; }
  bool MonsterHasPain() { return true; }

  void MonsterKilled(entvars_t@ pevAttacker, int iGib) {
    g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_GIB, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));
    g_EntityFuncs.SpawnRandomGibs(self.pev, 1, 1);
    g_EntityFuncs.Remove(self);
    return;
  }

  void HandleAnimEvent(MonsterEvent@ pEvent) {
    switch (pEvent.event) {
      case ZOMBIE_CRUCIFIED_IDLE_SOUND:
        if (Math.RandomFloat(0.0, 1.0) < 0.05)
          g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_IDLEC, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));
        self.pev.framerate = Math.RandomFloat(0.5, 1.1); // randomize animation speed
        break;

      case ZOMBIE_RUN_IDLE_SOUND:
      case ZOMBIE_WALK_IDLE_SOUND:
        if (Math.RandomFloat(0.0, 1.0) < 0.05)
          g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_IDLE, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 73 + Math.RandomLong(0, 0x1f));
        break;

      case ZOMBIE_RIGHT_ATTACK:
        ThrowMeat(1, Vector(-10, 22, 30));
        break;

      case ZOMBIE_LEFT_ATTACK:
        ThrowMeat(2, Vector( -10, -24, 29));
        break;

      case ZOMBIE_FALL_SOUND:
        if (self.pev.radsuit_finished == 0.0)
          g_SoundSystem.EmitSoundDyn(self.edict(), CHAN_VOICE, Q1_ZOMBIE_DEATH, Math.RandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + Math.RandomLong(0, 0xf));
        break;

      case ZOMBIE_TEMP_DEAD:
        if (self.pev.radsuit_finished == 0.0) {
          SetThink(ThinkFunction(MonsterDefeated));
          self.pev.nextthink = g_Engine.time + 5.0;
          self.StopAnimation(); // stop the animation!
          self.pev.solid = SOLID_NOT;
          self.pev.radsuit_finished = 1.0;
        }
        break;

      default:
        BaseClass.HandleAnimEvent(pEvent);
    }
  }
}

void q1_RegisterMonster_ZOMBIE() {
  g_CustomEntityFuncs.RegisterCustomEntity("monster_qzombie", "monster_qzombie");
}
