#ifndef Tank_hpp
#define Tank_hpp

#include <logic/Logic.hpp>
#include "Car.hpp"

struct TankMesh : BoxMesh {
  const double size;
  const Car::Wheels wheels;
  const struct Head : CylinderMesh {
    const Vector position;
    Head(
      const Color& color,
      const double size
    );
  } head;
  struct Gun : CylinderMesh {
    const Vector position;
    Gun(
      const Color& color,
      const double size
    );
    Gun& setCenterColor(const Color& color);
  private:
    Vertex* center = nullptr;
  } gun;
  TankMesh(
    const Color& color,
    const double size
  );
};

template <
  typename fireType = Fire,
  const unsigned int fireCount = 3,
  typename parentType = Car
> struct Tank : Gunner<
  fireType,
  fireCount,
  parentType
> {
  Tank(
    const Color& color = Color::White,
    const double size = 1
  ) : mesh(
    TankMesh(
      color,
      size
    )
  ) {}
protected:
  Object& simulate(const Object::Time dt) override {
    if (
      this->isActive()
    ) {
      mesh.gun.setCenterColor(
        Color(Color::Magenta) *= sin(
          Logic::get()->getTimer() * 3
        )
      );
    }
    return Gunner<
      fireType,
      fireCount,
      parentType
    >::simulate(dt);
  }
private:
  TankMesh mesh;
  const Mesh& getMesh() const override {
    return mesh;
  }
  const double getWheelR() const override {
    return mesh.wheels.r;
  }
  const double getWheelsY() const override {
    return - 0.175 * mesh.size;
  }
  const Car& renderWheels() const override {
    const double rad = Logic::get()->getTimer() * Math::PI / 3;
    for (
      int i = - 1;
      i <= 1;
      i++
    ) {
      mesh.wheels.render(
        0.35 * mesh.size * i,
        rad
      );
    }
    return *this;
  }
  const Car& renderBody() const override {
    const Car& ret = Gunner<
      fireType,
      fireCount,
      parentType
    >::renderBody();
    {
      static Matrix m;
      static Matrix r;
      Graphics::get()->push().apply(
        m.setTranslate(
          mesh.head.position.x,
          mesh.head.position.y,
          mesh.head.position.z
        ) *= r.setYRotation(
          cos(
            Logic::get()->getTimer()
          ) * Math::PI / 6
        )
      );
      mesh.head.render();
      Graphics::get()->push().apply(
        m.setTranslate(
          mesh.gun.position.x,
          mesh.gun.position.y,
          mesh.gun.position.z
        ) *= r.setXRotation(
          cos(
            Logic::get()->getTimer() * 2
          ) * Math::PI / 12
        )
      );
      mesh.gun.render();
      Graphics::get()->pop().pop();
    }
    return ret;
  }
};

typedef FirstPerson::Gunner<
  FirstPerson::Enemy<
    Walker<
      Terminatable<
        ::Enemy<
          Tank<FirstPerson::EnemyFire>
        >
      >
    >
  >
> TankEnemyParent;

struct TankEnemy : TankEnemyParent {
  static const EnemyAction Fire = Turn + 3;
  using TankEnemyParent::TankEnemyParent;
protected:
  const bool getIsLeftTurn() const override;
  const bool heroIsVisible() const override;
  Object& setAction(const EnemyAction action) override;
  Object& simulate(
    const EnemyAction action,
    const Object::Time dt
  ) override;
};

#endif
