#ifndef Enemy_hpp
#define Enemy_hpp

#include "Fire.hpp"
#include <sound/Sound.hpp>

typedef unsigned int EnemyAction;

template <typename parentType>
struct Enemy : parentType {
  using parentType::parentType;
protected:
  static const EnemyAction Stop = 0;
  const Object::Time getEnemyTimer() const {
    return timer;
  }
  const bool is(const EnemyAction action) const {
    return this->action == action;
  }
  virtual Object& setAction(const EnemyAction action) {
    if (
      (this->action = action) == Stop
    ) {
      this->setSpeed();
    }
    return *this;
  }
  virtual Object& simulate(
    const EnemyAction action,
    const Object::Time dt
  ) = 0;
  Object& simulate(const Object::Time dt) override {
    if (
      this->isActive()
    ) {
      timer += dt;
      simulate(
        action,
        dt
      );
    } else if (
      (timer -= dt) <= 0
    ) {
      if (
        const Matrix* m = getActivationMatrix()
      ) {
        this->setPosition(m->_vector.position).setDirection(m->_vector.direction);
        setAction(Stop).setActive();
      }
    }
    return parentType::simulate(dt);
  }
  Object& setActive(const bool active) override {
    if (active) {
      timer = 0;
      if (activations++ > 0) {
        Sound::life();
      }
    } else {
      timer = getActivationTime();
    }
    return parentType::setActive(active);
  }
  virtual const Object::Time getActivationTime() const {
    return 3 + 4 * Math::rand();
  };
  virtual const Matrix* getActivationMatrix() = 0;
private:
  unsigned int activations = 0;
  EnemyAction action = Stop;
  Object::Time timer = 0;
};

template <typename parentType>
struct Terminatable : parentType {
  using parentType::parentType;
protected:
  static const EnemyAction Termination = parentType::Stop + 1;
  virtual const Vector& getTerminationSpeed() const {
    static Vector ret;
    ret.zero().y = this->getRxz() * 7 * (
      3 + Math::rand()
    ) / 2;
    return ret;
  }
  Object& setAction(const EnemyAction action) override {
    switch (action) {
      case Termination:
        this->setSpeed(
          getTerminationSpeed()
        );
        Sound::bang();
      case this->Stop:
        this->matrix.setIdentity();
    }
    return parentType::setAction(action);
  }
  using parentType::simulate;
  Object& simulate(
    const EnemyAction action,
    const Object::Time dt
  ) override {
    if (action == Termination) {
      if (
        this->getSpeed().y <= 0
      ) {
        this->setAction(this->Stop).setActive(false);
      } else {
        this->matrix.setXRotation(
          Math::PI * 3 * this->getEnemyTimer()
        );
      }
    }
    return *this;
  }
  Object& onCollision(Object* object) override {
    if (
      this->is(Termination) && (
        (
          object == nullptr && this->getSpeed().y <= 0
        ) || (
          object != nullptr && object->isStatic()
        )
      )
    ) {
      this->setAction(this->Stop).setActive(false);
    }
    return parentType::onCollision(object);
  }
private:
  const bool isTransparent() const override {
    return this->is(Termination);
  }
};

template <typename parentType>
struct Walker : parentType {
  static const EnemyAction Walk = parentType::Termination + 1;
  static const EnemyAction Turn = parentType::Termination + 2;
  using parentType::parentType;
protected:
  virtual const bool getIsLeftTurn() const {
    return Math::checkRand();
  }
  Object& setAction(const EnemyAction action) override {
    if (action == Turn) {
      this->setAction(this->Stop);
      isLeftTurn = getIsLeftTurn();
    }
    return parentType::setAction(action);
  }
  using parentType::simulate;
  Object& simulate(
    const EnemyAction action,
    const Object::Time dt
  ) override {
    static Vector v;
    switch (action) {
      case Turn:
        this->setDirection(
          (
            v = this->getDirection()
          ).rotateY(
            Math::PI * dt * (isLeftTurn ? -1 : 1)
          )
        );
        break;
      case this->Stop:
        return this->setAction(Walk);
    }
    return parentType::simulate(
      action,
      dt
    );
  }
private:
  bool isLeftTurn = false;
};

#endif
