#ifndef TanksEnemy_hpp
#define TanksEnemy_hpp

#include <play/Tank.hpp>

namespace Tanks {
  template <typename parentType>
  struct Walker : parentType {
    Walker(
      const double x1,
      const double z1,
      const double x2,
      const double z2,
      const Color& color = Color::White,
      const double size = 1
    ) : parentType(
      color,
      size
    ), a(
      Vector(
        x1,
        this->getRy(),
        z1
      )
    ), b(
      Vector(
        x2,
        this->getRy(),
        z2
      )
    ) {}
  protected:
    const Vector* from = nullptr;
    const Vector* to = nullptr;
    virtual const bool keepWalking() const {
      return true;
    }
    Object& simulate(
      const EnemyAction action,
      const Object::Time dt
    ) override {
      if (
        from != nullptr && to != nullptr && keepWalking()
      ) {
        static Vector v;
        switch (action) {
          case this->Walk:
            if (
              this->getPosition().getDistance(*from) >= from->getDistance(*to)
            ) {
              return uTurn();
            } else {
              this->lookAt(*to);
              (
                v = this->getDirection()
              ) *= this->getRxz() * 3;
              this->setSpeed(
                v.x,
                v.z
              );
            }
            break;
          case this->Turn:
            if (
              (
                (v = *to) -= this->getPosition()
              ).normalize().getDistance(
                this->getDirection()
              ) < 0.1
            ) {
              return this->setAction(this->Walk);
            }
        }
      }
      return parentType::simulate(
        action,
        dt
      );
    }
    const Matrix* getActivationMatrix() override {
      if (a != b) {
        for (
          unsigned int i = 0;
          i < 3;
          i++
        ) {
          static Vector v;
          (
            (
              (v = b) -= a
            ) *= Math::rand()
          ) += a;
          std::vector<Object*> collisions;
          if (
            this->getCollisions(
              collisions,
              v
            ) == 0
          ) {
            static Matrix ret;
            ret.setIdentity();
            ret._vector.position = v;
            if (
              v.getDistance(a) > v.getDistance(b)
            ) {
              from = &b;
              to = &a;
            } else {
              from = &a;
              to = &b;
            }
            (
              ret._vector.direction = *to
            ) -= *from;
            return &ret;
          }
        }
      }
      return nullptr;
    }
    Object& onCollision(Object* object) override {
      if (
        object != nullptr && dynamic_cast<const Walker*>(object)
      ) {
        if (
          this->getDistance(a) > this->getDistance(b)
        ) {
          from = &b;
          to = &a;
        } else {
          from = &a;
          to = &b;
        }
        return this->setAction(this->Turn);
      }
      return parentType::onCollision(object);
    }
  private:
    const Vector a;
    const Vector b;
    Object& uTurn() {
      if (from != nullptr && *from == a) {
        from = &b;
        to = &a;
      } else {
        from = &a;
        to = &b;
      }
      return this->setAction(this->Turn);
    }
  };
  template <typename parentType>
  struct ScoresDependent : parentType {
    ScoresDependent(
      const double x1,
      const double z1,
      const double x2,
      const double z2,
      const unsigned int scores = 0,
      const Color& color = Color::White,
      const double size = 1
    ) : parentType(
      x1,
      z1,
      x2,
      z2,
      color,
      size
    ), scores(scores) {}
  private:
    const unsigned int scores;
    const Matrix* getActivationMatrix() override {
      return FirstPerson::Logic::get()->scores < scores ? nullptr : parentType::getActivationMatrix();
    }
  };
  struct Enemy : ScoresDependent<
    Walker<TankEnemy>
  > {
    using ScoresDependent::ScoresDependent;
  private:
    unsigned int count = 0;
    Time timer = 0;
    Object& simulate(
      const EnemyAction action,
      const Object::Time dt
    ) override;
    const double getFireStartSpeed() const override;
    const bool getIsLeftTurn() const override;
    const bool heroIsVisible() const override;
    const bool keepWalking() const override;
    Object& onFire() override;
  };
}

#endif
