#ifndef Fire_hpp
#define Fire_hpp

#include "Object.hpp"

class Fire : public MeshObject {
  double rotation;
  const bool isTransparent() const override;
  const Mesh& getMesh() const override;
  Object& setActive(const bool active) override;
  Object& simulate(const Time dt) override;
protected:
  static const struct ConeMesh : ::ConeMesh {
    ConeMesh(const Color& color = Color::Magenta);
  } coneMesh;
  static const struct LineMesh : ::LineMesh {
    LineMesh(const Color& color = Color::Magenta);
  } lineMesh;
  Object& onCollision(Object* object) override;
};

template <
  typename fireType,
  const unsigned int size,
  typename parentType = MeshObject
> class Gunner : public parentType {
  Object::Time timer = 0;
  fireType fires[size];
protected:
  virtual const double getFireMistake() const {
    return 1;
  }
  virtual const Object::Time getFireTime() const {
    return 0;
  }
  virtual const double getFireStartSpeed() const {
    return Graphics::get()->R;
  }
  virtual const Vector& getFireStartDirection() const {
    return this->getDirection();
  }
  virtual Object& onFire() {
    return *this;
  }
public:
  virtual Object& fire() {
    if (timer <= 0) {
      timer = getFireTime();
      for (
        unsigned int i = 0;
        i < size;
        i++
      ) {
        fireType* fire = &fires[i];
        if (
          !fire->isActive()
        ) {
          static Vector s;
          (
            s = getFireStartDirection()
          ).rotateY(
            Math::PIdiv4 * Math::rand(-0.5) * getFireMistake()
          ).y = 0.125 * (
            1 + Math::rand() * getFireMistake()
          );
          s.normalize() *= getFireStartSpeed();
          static Vector d;
          (d = s).y = 0;
          d.normalize();
          static Vector p;
          (
            (p = d) *= (
              this->getRxz() + fire->getRxz()
            )
          ) += this->getPosition();
          fire->setDirection(d).setPosition(p).setActive().setSpeed(s);
          return onFire();
        }
      }
    }
    return *this;
  }
  Object& simulate(const Object::Time dt) override {
    if (timer > 0) {
      timer -= dt;
    }
    return parentType::simulate(dt);
  }
};

#endif
