#include "layout.hpp"
#include "algebra.hpp"
#include <cmath>
#include <limits>

namespace nodesoup {

    using std::vector;

    void circle(const adj_list_t& g, vector<Point2D>& positions) {
        const double pi = 3.14159265358979323846;
        double angle = 2.0 * pi / g.size();
        for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
            positions[v_id].x = cos(v_id * angle);
            positions[v_id].y = sin(v_id * angle);
        }
    }

    void center_and_scale(const adj_list_t& g, unsigned int width, unsigned int height, vector<Point2D>& positions) {
        // find current dimensions
        double x_min = std::numeric_limits<double>::max();
        double x_max = std::numeric_limits<double>::lowest();
        double y_min = std::numeric_limits<double>::max();
        double y_max = std::numeric_limits<double>::lowest();

        for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
            if (positions[v_id].x < x_min)
                x_min = positions[v_id].x;
            if (positions[v_id].x > x_max)
                x_max = positions[v_id].x;
            if (positions[v_id].y < y_min)
                y_min = positions[v_id].y;
            if (positions[v_id].y > y_max)
                y_max = positions[v_id].y;
        }

        double cur_width = x_max - x_min;
        double cur_height = y_max - y_min;

        // compute scale factor (0.9: keep some margin)
        double x_scale = (cur_width > 0) ? width / cur_width : width;
        double y_scale = (cur_height > 0) ? height / cur_height : height;
        Vector2D scale = { x_scale, y_scale };

        // compute offset and apply it to every position
        Vector2D center = { x_max + x_min, y_max + y_min };
        Vector2D offset = (center / 2.0) * scale;

        double shiftMinX = std::numeric_limits<double>::max();
        double shiftMinY = std::numeric_limits<double>::max();

        for (vertex_id_t v_id = 0; v_id < g.size(); v_id++)
        {
            positions[v_id] = (Point2D)((Vector2D)positions[v_id] * scale - offset);
            shiftMinX = std::min(shiftMinX, positions[v_id].x);
            shiftMinY = std::min(shiftMinY, positions[v_id].y);
        }

        shiftMinX = abs(shiftMinX) + 50;
        shiftMinY = abs(shiftMinY) + 50;

        for (auto& pos : positions)
        {
            pos.x += shiftMinX;
            pos.y += shiftMinY;
        }
    }
}
