#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "figure-draw.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

/* Вспомогательная функция для установки пикселя */
static inline void set_pixel(uint8_t *data, int32_t width, int32_t height, int x, int y, uint32_t color)
{
    if (x < 0 || x >= width || y < 0 || y >= height)
        return;
    
    uint32_t *pixel = (uint32_t *)(data + (y * width + x) * 4);
    *pixel = color;
}

/* Проверка, находится ли точка внутри треугольника (барицентрические координаты) */
static int point_in_triangle(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3)
{
    float d1 = (px - x2) * (y1 - y2) - (x1 - x2) * (py - y2);
    float d2 = (px - x3) * (y2 - y3) - (x2 - x3) * (py - y3);
    float d3 = (px - x1) * (y3 - y1) - (x3 - x1) * (py - y1);
    
    int has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
    int has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
    
    return !(has_neg && has_pos);
}

/* Расстояние от точки до отрезка */
static float point_to_segment_distance(float px, float py, float x1, float y1, float x2, float y2)
{
    float dx = x2 - x1;
    float dy = y2 - y1;
    float len_sq = dx * dx + dy * dy;
    
    if (len_sq < 0.0001f) {
        dx = px - x1;
        dy = py - y1;
        return sqrtf(dx * dx + dy * dy);
    }
    
    float t = ((px - x1) * dx + (py - y1) * dy) / len_sq;
    t = fmaxf(0.0f, fminf(1.0f, t));
    
    float proj_x = x1 + t * dx;
    float proj_y = y1 + t * dy;
    
    dx = px - proj_x;
    dy = py - proj_y;
    
    return sqrtf(dx * dx + dy * dy);
}

/* Рисование круга */
static void draw_circle(struct window_draw_info* draw_info, float cx, float cy, float radius,
                        float border_thickness, uint32_t border_color, uint32_t fill_color)
{
    int x_min = (int)fmaxf(0, cx - radius - border_thickness);
    int x_max = (int)fminf(draw_info->width - 1, cx + radius + border_thickness);
    int y_min = (int)fmaxf(0, cy - radius - border_thickness);
    int y_max = (int)fminf(draw_info->height - 1, cy + radius + border_thickness);
    
    for (int y = y_min; y <= y_max; y++) {
        for (int x = x_min; x <= x_max; x++) {
            float dx = x - cx;
            float dy = y - cy;
            float dist = sqrtf(dx * dx + dy * dy);
            
            if (dist <= radius) {
                set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
            } else if (dist <= radius + border_thickness) {
                set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
            }
        }
    }
}

/* Рисование треугольника */
static void draw_triangle(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle,
                          float border_thickness, uint32_t border_color, uint32_t fill_color)
{
    /* Вычисляем координаты вершин равностороннего треугольника */
    /* Угол 0 означает, что одна вершина справа от центра */
    float vertices[3][2];
    for (int i = 0; i < 3; i++) {
        float vertex_angle = angle + i * (2.0f * M_PI / 3.0f);
        vertices[i][0] = cx + radius * cosf(vertex_angle);
        vertices[i][1] = cy - radius * sinf(vertex_angle);  /* Инвертируем Y для экранных координат */
    }
    
    /* Находим ограничивающий прямоугольник */
    float min_x = fminf(vertices[0][0], fminf(vertices[1][0], vertices[2][0])) - border_thickness;
    float max_x = fmaxf(vertices[0][0], fmaxf(vertices[1][0], vertices[2][0])) + border_thickness;
    float min_y = fminf(vertices[0][1], fminf(vertices[1][1], vertices[2][1])) - border_thickness;
    float max_y = fmaxf(vertices[0][1], fmaxf(vertices[1][1], vertices[2][1])) + border_thickness;
    
    int x_min = (int)fmaxf(0, min_x);
    int x_max = (int)fminf(draw_info->width - 1, max_x);
    int y_min = (int)fmaxf(0, min_y);
    int y_max = (int)fminf(draw_info->height - 1, max_y);
    
    /* Рисуем треугольник */
    for (int y = y_min; y <= y_max; y++) {
        for (int x = x_min; x <= x_max; x++) {
            int inside = point_in_triangle((float)x, (float)y,
                                          vertices[0][0], vertices[0][1],
                                          vertices[1][0], vertices[1][1],
                                          vertices[2][0], vertices[2][1]);
            
            if (inside) {
                set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
            } else {
                /* Проверяем расстояние до границ */
                float dist1 = point_to_segment_distance((float)x, (float)y,
                                                       vertices[0][0], vertices[0][1],
                                                       vertices[1][0], vertices[1][1]);
                float dist2 = point_to_segment_distance((float)x, (float)y,
                                                       vertices[1][0], vertices[1][1],
                                                       vertices[2][0], vertices[2][1]);
                float dist3 = point_to_segment_distance((float)x, (float)y,
                                                       vertices[2][0], vertices[2][1],
                                                       vertices[0][0], vertices[0][1]);
                
                float min_dist = fminf(dist1, fminf(dist2, dist3));
                if (min_dist <= border_thickness) {
                    set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
                }
            }
        }
    }
}

/* Рисование квадрата */
static void draw_square(struct window_draw_info* draw_info, float cx, float cy, float radius, float angle,
                        float border_thickness, uint32_t border_color, uint32_t fill_color)
{
    /* Вычисляем координаты вершин квадрата */
    /* Угол 0 означает, что одна вершина справа от центра */
    float vertices[4][2];
    for (int i = 0; i < 4; i++) {
        float vertex_angle = angle + i * (M_PI / 2.0f);
        vertices[i][0] = cx + radius * cosf(vertex_angle);
        vertices[i][1] = cy - radius * sinf(vertex_angle);  /* Инвертируем Y для экранных координат */
    }
    
    /* Находим ограничивающий прямоугольник */
    float min_x = vertices[0][0], max_x = vertices[0][0];
    float min_y = vertices[0][1], max_y = vertices[0][1];
    for (int i = 1; i < 4; i++) {
        min_x = fminf(min_x, vertices[i][0]);
        max_x = fmaxf(max_x, vertices[i][0]);
        min_y = fminf(min_y, vertices[i][1]);
        max_y = fmaxf(max_y, vertices[i][1]);
    }
    
    min_x -= border_thickness;
    max_x += border_thickness;
    min_y -= border_thickness;
    max_y += border_thickness;
    
    int x_min = (int)fmaxf(0, min_x);
    int x_max = (int)fminf(draw_info->width - 1, max_x);
    int y_min = (int)fmaxf(0, min_y);
    int y_max = (int)fminf(draw_info->height - 1, max_y);
    
    /* Рисуем квадрат */
    for (int y = y_min; y <= y_max; y++) {
        for (int x = x_min; x <= x_max; x++) {
            float px = (float)x;
            float py = (float)y;
            
            /* Проверяем, находится ли точка внутри квадрата */
            /* Используем два треугольника */
            int inside = point_in_triangle(px, py,
                                          vertices[0][0], vertices[0][1],
                                          vertices[1][0], vertices[1][1],
                                          vertices[2][0], vertices[2][1]) ||
                        point_in_triangle(px, py,
                                          vertices[0][0], vertices[0][1],
                                          vertices[2][0], vertices[2][1],
                                          vertices[3][0], vertices[3][1]);
            
            if (inside) {
                set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, fill_color);
            } else {
                /* Проверяем расстояние до границ */
                float min_dist = INFINITY;
                for (int i = 0; i < 4; i++) {
                    int next = (i + 1) % 4;
                    float dist = point_to_segment_distance(px, py,
                                                          vertices[i][0], vertices[i][1],
                                                          vertices[next][0], vertices[next][1]);
                    min_dist = fminf(min_dist, dist);
                }
                
                if (min_dist <= border_thickness) {
                    set_pixel(draw_info->data, draw_info->width, draw_info->height, x, y, border_color);
                }
            }
        }
    }
}

void figure_draw(struct window_draw_info* draw_info, float border_thickness, uint32_t border_color, uint32_t fill_color)
{
    if (!draw_info || !draw_info->data)
        return;
    
    /* Координаты приходят в относительных единицах
     * Y: [0..1] -> умножаем на высоту
     * X: нормализован относительно высоты -> умножаем на высоту же
     */
    float cx = draw_info->figure.position.x * draw_info->height;
    float cy = draw_info->figure.position.y * draw_info->height;
    float radius = draw_info->figure.radius;
    float angle = draw_info->figure.angle;
    enum figure_type type = draw_info->figure.type;
    
    switch (type) {
        case FIGURE_CIRCLE:
            draw_circle(draw_info, cx, cy, radius, border_thickness, border_color, fill_color);
            break;
            
        case FIGURE_TRIANGLE:
            draw_triangle(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
            break;
            
        case FIGURE_SQUARE:
            draw_square(draw_info, cx, cy, radius, angle, border_thickness, border_color, fill_color);
            break;
    }
}

