#include "tiles/tile.hpp"
#include "decorations/decoration.hpp"

#include "space.hpp"

#include "playstate.hpp"
#include "stage.hpp"

Space::Space(const KungFlowGame *game, const PlayState *playState, const Stage *stage, const Stadium *stadium, int x, int y, int w, int h) : x(x), y(y), w(w), h(h), game(game), playState(playState), stage(stage), stadium(stadium) {
}

Space::~Space() {
  for (unsigned int i = 0; i < decorationsBack.size(); i++) {
    delete decorationsBack.at(i);
  }
  for (unsigned int i = 0; i < decorationsFront.size(); i++) {
    delete decorationsFront.at(i);
  }
  for (unsigned int i = 0; i < tiles.size(); i++) {
    delete tiles.at(i);
  }
}

const Tile *Space::GetTile(int xpos, int ypos) const {
  if (xpos < this->x || xpos >= this->x + this->w || ypos < this->y || ypos >= this->y + this->h) {
    return nullptr; // out of physical bounds
  }
  int index = (xpos - this->x) * this->h + (ypos - this->y);
  if (index >= 0 && index < (signed int)tiles.size()) { // within array bounds
    return tiles.at(index);
  } else {
    return nullptr;
  }
}

const Tile *Space::GetTile(const Vector2 &position) const {
  return GetTile(int(round(position.coords[0])), int(round(position.coords[1])));
}

bool Space::IsTraversable(int xpos, int ypos) const {
  if (xpos < this->x || xpos >= this->x + this->w || ypos < this->y || ypos >= this->y + this->h) {
    return false; // out of physical bounds
  }
  int index = (xpos - this->x) * this->h + (ypos - this->y);
  if (index >= 0 && index < (signed int)tiles.size()) { // within array bounds
    return tiles.at(index)->IsTraversable();
  } else {
    return false;
  }
}

bool Space::IsTraversable(const Vector2 &position) const {
  return IsTraversable(int(round(position.coords[0])), int(round(position.coords[1])));
}


// ROOFTOP SPACE

RooftopSpace::RooftopSpace(const KungFlowGame *game, const PlayState *playState, const Stage *stage, const Stadium *stadium, int x, int y, int w, int h, int z) : Space(game, playState, stage, stadium, x, y, w, h), baseZPosition(z) {

  for (int i = x; i < x + w; i++) {
    for (int j = y; j < y + h; j++) {

      /*
      float augmentZScalar = 1.0f;
      float augmentZX = sin((i / 4) * pi * 0.6f) * 0.5f;
      float augmentZY = sin((j / 2) * pi * 1.1f + 1.5f) * 0.5f;
      float augmentZ = (augmentZX + augmentZY) * 0.5f * augmentZScalar;
      */
      float augmentZ = 0.0f;
      //augmentZ = ((abs(j) / 2) * 2) * 0.1f;
      //if (i < x + 3 && abs(j) < 3) augmentZ = 0.5f;

      //Object *object = new Object(&playState->GetWorldNode(), ((i + j) % 2 == 0) ? isoTileSprite1 : isoTileSprite3);
      bool darkTile = false;
      if (i == x || i == x + w - 1 || j == y || j == y + h - 1) darkTile = true; // edges

      bool traversable = true;
      Tile *tile = new Tile(playState, darkTile ? "res/gfx/building1_top1.png" : "res/gfx/building2_top2.png", e_FilterMode_Nearest, i, j, z + augmentZ, traversable);
      tiles.push_back(tile);

      // side of building
      if (i == x + w - 1 || j == y + h - 1) {
        Vector2 buildingSidePosition = Vector2(i, j);
        float height = 3.0f;
        std::string sidePng = ((x + y + w + h + z) % 2 == 0) ? "res/gfx/building1_side1.png" : "res/gfx/building2_side1.png";
        Decoration *deco = new Decoration(playState, sidePng, e_FilterMode_Anisotropic, buildingSidePosition, z - height * 0.5f, height);
        decorationsBack.push_back(deco);

        deco = new Decoration(playState, sidePng, e_FilterMode_Anisotropic, buildingSidePosition, z - height - height * 0.5f, height);
        decorationsBack.push_back(deco);

      }

    }
  }

}

RooftopSpace::~RooftopSpace() {

  /* the Tile objects in the tiles and decoration arrays will be deleted by the Space class' destructor
  for (unsigned int i = 0; i < decorations.size(); i++) {
    delete decorations.at(i);
  }
  for (unsigned int i = 0; i < tiles.size(); i++) {
    delete tiles.at(i);
  }
  */

}

void RooftopSpace::OnStepPhysicsTime() {
  for (unsigned int i = 0; i < tiles.size(); i++) {
    tiles.at(i)->OnStepPhysicsTime();
  }
  for (unsigned int i = 0; i < decorationsBack.size(); i++) {
    decorationsBack.at(i)->OnStepPhysicsTime();
  }
  for (unsigned int i = 0; i < decorationsFront.size(); i++) {
    decorationsFront.at(i)->OnStepPhysicsTime();
  }
}

/*
void RooftopSpace::PostStepPhysicsTime() {
  for (unsigned int i = 0; i < decorations.size(); i++) {
    decorations.at(i)->PostStepPhysicsTime();
  }
  for (unsigned int i = 0; i < tiles.size(); i++) {
    tiles.at(i)->PostStepPhysicsTime();
  }
}
*/

void RooftopSpace::GetPhysicsObjects(std::vector<PhysicsObject*> &targetContainer) const {
//  for (unsigned int i = 0; i < decorations.size(); i++) {
//    targetContainer.push_back(decorations.at(i));
//  }
  for (unsigned int i = 0; i < tiles.size(); i++) {
    targetContainer.push_back(tiles.at(i));
  }
}

void RooftopSpace::Draw(const Viewport &viewport, const Camera &camera) const {
  /*
  playState->DrawLine(Vector2(x, y + h - 1) - baseZPosition,
                      Vector2(x, y + h - 1) - 0, e_CoordinateSystem_World, 1, 0, 1, 0.8f);

  playState->DrawLine(Vector2(x + w - 1, y) - baseZPosition,
                      Vector2(x + w - 1, y) - 0, e_CoordinateSystem_World, 1, 0, 1, 0.8f);

  playState->DrawLine(Vector2(x + w - 1, y + h - 1) - baseZPosition,
                      Vector2(x + w - 1, y + h - 1) - 0, e_CoordinateSystem_World, 1, 0, 1, 0.8f);
  */
}

void RooftopSpace::DrawBackLayer(const Viewport &viewport, const Camera &camera) const {
  for (unsigned int i = 0; i < decorationsBack.size(); i++) {
    decorationsBack.at(i)->Draw(viewport, camera);
  }
}

void RooftopSpace::DrawFrontLayer(const Viewport &viewport, const Camera &camera) const {
  for (unsigned int i = 0; i < decorationsFront.size(); i++) {
    decorationsFront.at(i)->Draw(viewport, camera);
  }
}

void RooftopSpace::DrawLights() const {

  //printf("%i, %i\n", x + w / 2, y + h / 2);
  //stage->DrawLight(Vector2(x + (w - 1) / 2.0f, y + (h - 1) / 2.0f) - baseZPosition, 0, 5.0f, 0.5f, 0.5f, 0.5f);
  /*
  if (GetCenterPosition().coords[stage->GetStadium()->GetFlowAxis()] + 5 < stage->GetStadium()->GetFlowPosition().coords[stage->GetStadium()->GetFlowAxis()]) {
    stage->DrawLight(Vector2(x + (w - 1) / 2.0f, y + (h - 1) / 2.0f) - baseZPosition, 0, 14.0f, 1.0f, 0.7f, 0.3f);
  } else {
    stage->DrawLight(Vector2(x + (w - 1) / 2.0f, y + (h - 1) / 2.0f) - baseZPosition, 0, 10.0f, 0.0f, 0.2f, 0.3f);
  }*/
  stage->DrawLight(Vector2(x + (w - 1) / 2.0f, y + (h - 1) / 2.0f) - baseZPosition, 0, 1.0f + sqrt(w * h) * 0.5f, 0.05f, 0.25f, 0.3f);

  stage->DrawLight(Vector2(x + 2    , y + 2    ) - baseZPosition, 0, 1.0f + sqrt(w * h) * 0.2f, 0.2f, 0.25f, 0.23f);
  stage->DrawLight(Vector2(x + 2    , y + h - 3) - baseZPosition, 0, 1.0f + sqrt(w * h) * 0.2f, 0.2f, 0.25f, 0.23f);
  stage->DrawLight(Vector2(x + w - 3, y + 2    ) - baseZPosition, 0, 1.0f + sqrt(w * h) * 0.2f, 0.2f, 0.25f, 0.23f);
  stage->DrawLight(Vector2(x + w - 3, y + h - 3) - baseZPosition, 0, 1.0f + sqrt(w * h) * 0.2f, 0.2f, 0.25f, 0.23f);

  // glare on puddles ('neon reflections from the city' or whatever :p)

  srand(x * y + w * h + baseZPosition);
  float glareIntensity = 0.25f;
  int glarePuddlesPerRoof = 2 + sqrt(w * h) * 0.5f;
  float r, g, b;
  r = Random(0.5f, 0.9f) * glareIntensity;
  g = Random(0.4f, 0.8f) * glareIntensity;
  b = Random(0.1f, 0.6f) * glareIntensity;
  for (int i = 0; i < glarePuddlesPerRoof; i++) {
    stage->DrawLight(Vector2(Random(x, x + w - 1), Random(y, y + h - 1)), baseZPosition, 2.0f, r, g, b);
  }
  srand(game->GetPhysicsTime_ms());// + x * y + w * h + baseZPosition); // reseed

  // city lights
  Vector2 cityLightPos = GetCenterPosition();
  cityLightPos.coords[abs(1 - stadium->GetFlowAxis())] = +11;
  //stage->DrawLight(cityLightPos, baseZPosition, 10.0f, 0.8f, 0.4f, 0.2f);
  stage->DrawLight(cityLightPos, baseZPosition, 10.0f, 0.8f, 0.5f, 0.3f);
  cityLightPos.coords[abs(1 - stadium->GetFlowAxis())] = -11;
  //stage->DrawLight(cityLightPos, baseZPosition, 10.0f, 0.8f, 0.4f, 0.2f);
  stage->DrawLight(cityLightPos, baseZPosition, 10.0f, 0.8f, 0.5f, 0.3f);

}

std::vector<Vector2> RooftopSpace::GetSpawnPositions() const {

  std::vector<Vector2> spawnPositions;

  int everyNthX = 3;
  int everyNthY = 3;

  int xNum = std::max(1, w / everyNthX);
  int yNum = std::max(1, h / everyNthY);
  float xStepSize = w / (float)xNum;
  float yStepSize = h / (float)yNum;
  //printf("%i %i %f %f\n", xNum, yNum, xStepSize, yStepSize);

  for (float xp = xStepSize * 0.5f; xp <= w; xp += xStepSize) {
    for (float yp = yStepSize * 0.5f; yp <= h; yp += yStepSize) {
      spawnPositions.push_back(Vector2(x + xp, y + yp));
    }
  }

  return spawnPositions;

}
