#include <play/Kicker.hpp>
#include <play/House.hpp>
#include <play/Tree.hpp>
#include <play/Hero.hpp>
#include <play/Sky.hpp>
#include "Mountains.hpp"
#include "Tree.hpp"
#include "Enemy.hpp"
#include "Fokker.hpp"

namespace {
  const unsigned int n = 10;
  const double r = 5.0;
  const double i2d(const unsigned int i) {
    return (0.5 + i - n / 2) * r;
  }
  const double treeRand() {
    return r * Math::rand(-0.5) / 7;
  }
}

namespace Tanks {
  class Logic : public FirstPerson::Logic {
    class Hero : public Play::Hero {
      const double getFireStartSpeed() const override {
        return Play::Hero::getFireStartSpeed() / 2;
      }
    };
    const Sky sky;
    const Mountains mountains;
    Vector& getFrom(Vector& from) const override {
      (
        (
          FirstPerson::Logic::getFrom(from) *= 2
        ) /= 3
      ).y /= 3;
      return from;
    }
    Vector& getTo(Vector& to) const override {
      return FirstPerson::Logic::getTo(to).neg() /= 2;
    }
    Graphics& renderObjects() override {
      sky.render();
      mountains.render();
      return FirstPerson::Logic::renderObjects();
    }
  public:
    Logic() : FirstPerson::Logic(
      "Tanks",
      n * r / 2,
      new Hero()
    ), sky(2) {
      {
        Object* object = new Kicker(0.35);
        object->setPosition(
          0,
          0
        ).setActive();
      }
      const double minR = r * 3 / 2;
      const double maxR = r * n / 2;
      for (
        unsigned int i = 0;
        i < n;
        i++
      ) {
        for (
          unsigned int j = 0;
          j < n;
          j++
        ) {
          Vector position = Vector(
            i2d(i),
            0,
            i2d(j)
          );
          if (
            position.getLength() < R - r / 3 && (
              std::abs(position.x) >= minR || std::abs(position.z) >= minR
            ) && Math::checkRand(0.85)
          ) {
            const double probability = 1 - (std::min)(
              1.0,
              (
                std::floor(
                  position.getLength()
                ) - minR
              ) / (maxR - minR)
            );
            if (
              Math::checkRand(probability * 2)
            ) {
              Object* object = nullptr;
              if (
                Math::checkRand(probability / 3)
              ) {
                object = new House(
                  0.25 + probability * 0.75,
                  0.5 + 0.25 * Math::rand()
                );
                if (
                  std::abs(position.x) > std::abs(position.z)
                ) {
                  object->setDirection(Vector::X);
                }
              } else {
                object = new Tree(
                  3 + probability * 2,
                  Math::PIdiv4 * (
                    0.5 + Math::rand()
                  ) / 10
                );
                object->setDirection(
                  Vector(position).rotateY(
                    Math::PImul2 * Math::rand()
                  )
                );
                position += Vector(
                  treeRand(),
                  0,
                  treeRand()
                );
              }
              object->setPosition(
                position.x,
                position.z
              ).setActive();
            }
          }
        }
      }
      for (
        unsigned int i = 0;
        i < 3;
        i++
      ) {
        const double min = i2d(1) + r / 2 + i * r;
        const double max = i2d(n - 2) - r / 2 - i * r;
        for (
          unsigned int j = 0;
          j < 4;
          j++
        ) {
          const unsigned int ds = 5;
          new Enemy(
            j % 2 ? max : min,
            j % 2 ? max : min,
            j % 3 ? min : max,
            j % 3 ? max : min,
            i < 1 ? (
              j < 2 ? 0 : ds * (j - 1)
            ) : (
              i < 2 ? (
                j < 2 ? ds * (1 + j) : 0
              ) : ds * (5 + j)
            )
          );
        }
        if (i < 2) {
          const bool b = i % 2;
          new FokkerEnemy(
            Vector(
              R / (b ? 3.0 / 2 : 2),
              R / (b ? 7 : 5)
            ),
            b
          );
        }
      }
    }
  };
}

Logic* Logic::create() {
  return new Tanks::Logic();
}
