#include "lights.hpp"
#include "constants.hpp"
#include <vector>

using std::vector;

namespace lighting {

  void LightRender::render_circle_light(LightSource source, Point at, PointList &has_light) {
    for(matrix::circle it{$lightmap, at, source.distance + 1}; it.next();) {
      for(int x = it.left; x < it.right; x++) {
        $lightmap[it.y][x] = light_level(source.strength, x, it.y);
        has_light.push_back({(size_t)x, (size_t)it.y});
      }
    }
  }

  void LightRender::render_compass_light(LightSource source, Point at, PointList &has_light) {
    for(matrix::compass it{$lightmap, at.x, at.y}; it.next();) {
      if($paths.$paths[it.y][it.x] != WALL_PATH_LIMIT) {
        $lightmap[it.y][it.x] = light_level(source.strength, it.x, it.y);
        has_light.push_back({it.x, it.y});
      }
    }
  }

  void LightRender::render_square_light(LightSource source, Point at, PointList &has_light) {
    for(matrix::in_box it{$lightmap, at.x, at.y, (size_t)source.distance}; it.next();) {
      if($paths.$paths[it.y][it.x] != WALL_PATH_LIMIT) {
        $lightmap[it.y][it.x] = light_level(source.strength, it.x, it.y);
        has_light.push_back({it.x, it.y});
      }
    }
  }

  void LightRender::render_light(LightSource source, Point at) {
    Point min, max;
    clear_light_target(at);
    PointList has_light;

    if(source.distance == 0) {
      render_compass_light(source, at, has_light);
    } else if(source.distance == 1) {
      render_square_light(source, at, has_light);
    } else {
      render_circle_light(source, at, has_light);
    }

    const int wall_light = source.strength + WALL_LIGHT_LEVEL;
    for(auto point : has_light) {
      for(matrix::compass it{$lightmap, point.x, point.y}; it.next();) {
        if($paths.$paths[it.y][it.x] == WALL_PATH_LIMIT) {
            $lightmap[it.y][it.x] = light_level(wall_light, point.x, point.y);
        }
      }
    }
  }

  int LightRender::light_level(int level, size_t x, size_t y) {
    size_t at = level + $paths.$paths[y][x];
    int cur_level = $lightmap[y][x];
    int new_level = at < lighting::LEVELS.size() ? lighting::LEVELS[at] : lighting::MIN;
    return cur_level < new_level ? new_level : cur_level;
  }


  void LightRender::reset_light() {
    for(auto &row : $lightmap) {
      row.assign(row.size(), lighting::MIN);
    }
  }

  void LightRender::clear_light_target(const Point &at) {
    $paths.clear_target(at);
  }

  void LightRender::set_light_target(const Point &at, int value) {
    $paths.set_target(at, value);
  }

  void LightRender::path_light(Matrix &walls) {
    $paths.compute_paths(walls);
  }
}
