#include "pxl8_transition.h"

#include <math.h>
#include <stdlib.h>

#include "pxl8_gfx.h"
#include "pxl8_log.h"
#include "pxl8_math.h"

pxl8_transition* pxl8_transition_create(pxl8_transition_type type, f32 duration) {
    if (duration <= 0.0f) {
        pxl8_error("Invalid transition duration: %f", duration);
        return NULL;
    }

    pxl8_transition* transition = (pxl8_transition*)calloc(1, sizeof(pxl8_transition));
    if (!transition) {
        pxl8_error("Failed to allocate transition");
        return NULL;
    }

    transition->type = type;
    transition->duration = duration;
    transition->time = 0.0f;
    transition->active = false;
    transition->reverse = false;
    transition->color = 0xFF000000;
    transition->on_complete = NULL;
    transition->userdata = NULL;

    return transition;
}

void pxl8_transition_destroy(pxl8_transition* transition) {
    if (!transition) return;
    free(transition);
}

f32 pxl8_transition_get_progress(const pxl8_transition* transition) {
    if (!transition) return 0.0f;

    f32 t = transition->time / transition->duration;
    if (t < 0.0f) t = 0.0f;
    if (t > 1.0f) t = 1.0f;

    return transition->reverse ? 1.0f - t : t;
}

bool pxl8_transition_is_active(const pxl8_transition* transition) {
    return transition && transition->active;
}

bool pxl8_transition_is_complete(const pxl8_transition* transition) {
    if (!transition) return true;
    return transition->time >= transition->duration;
}

void pxl8_transition_render(const pxl8_transition* transition, pxl8_gfx* gfx) {
    if (!transition || !gfx || !transition->active) return;

    f32 progress = pxl8_transition_get_progress(transition);
    i32 width = pxl8_gfx_get_width(gfx);
    i32 height = pxl8_gfx_get_height(gfx);

    switch (transition->type) {
        case PXL8_TRANSITION_FADE: {
            u8 alpha = (u8)(progress * 255.0f);
            u32 fade_color = (transition->color & 0x00FFFFFF) | (alpha << 24);

            for (i32 y = 0; y < height; y++) {
                for (i32 x = 0; x < width; x++) {
                    u32 bg = pxl8_2d_get_pixel(gfx, x, y);
                    u32 r_bg = (bg >> 16) & 0xFF;
                    u32 g_bg = (bg >> 8) & 0xFF;
                    u32 b_bg = bg & 0xFF;

                    u32 r_fg = (fade_color >> 16) & 0xFF;
                    u32 g_fg = (fade_color >> 8) & 0xFF;
                    u32 b_fg = fade_color & 0xFF;

                    u32 r = (r_bg * (255 - alpha) + r_fg * alpha) / 255;
                    u32 g = (g_bg * (255 - alpha) + g_fg * alpha) / 255;
                    u32 b = (b_bg * (255 - alpha) + b_fg * alpha) / 255;

                    pxl8_2d_pixel(gfx, x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
                }
            }
            break;
        }

        case PXL8_TRANSITION_WIPE_LEFT: {
            i32 wipe_x = (i32)(width * progress);
            pxl8_2d_rect_fill(gfx, 0, 0, wipe_x, height, transition->color);
            break;
        }

        case PXL8_TRANSITION_WIPE_RIGHT: {
            i32 wipe_x = (i32)(width * (1.0f - progress));
            pxl8_2d_rect_fill(gfx, wipe_x, 0, width - wipe_x, height, transition->color);
            break;
        }

        case PXL8_TRANSITION_WIPE_UP: {
            i32 wipe_y = (i32)(height * progress);
            pxl8_2d_rect_fill(gfx, 0, 0, width, wipe_y, transition->color);
            break;
        }

        case PXL8_TRANSITION_WIPE_DOWN: {
            i32 wipe_y = (i32)(height * (1.0f - progress));
            pxl8_2d_rect_fill(gfx, 0, wipe_y, width, height - wipe_y, transition->color);
            break;
        }

        case PXL8_TRANSITION_CIRCLE_CLOSE: {
            i32 center_x = width / 2;
            i32 center_y = height / 2;
            i32 max_radius = (i32)sqrtf((f32)(center_x * center_x + center_y * center_y));
            i32 radius = (i32)(max_radius * (1.0f - progress));

            for (i32 y = 0; y < height; y++) {
                for (i32 x = 0; x < width; x++) {
                    i32 dx = x - center_x;
                    i32 dy = y - center_y;
                    i32 dist = (i32)sqrtf((f32)(dx * dx + dy * dy));
                    if (dist > radius) {
                        pxl8_2d_pixel(gfx, x, y, transition->color);
                    }
                }
            }
            break;
        }

        case PXL8_TRANSITION_CIRCLE_OPEN: {
            i32 center_x = width / 2;
            i32 center_y = height / 2;
            i32 max_radius = (i32)sqrtf((f32)(center_x * center_x + center_y * center_y));
            i32 radius = (i32)(max_radius * progress);

            for (i32 y = 0; y < height; y++) {
                for (i32 x = 0; x < width; x++) {
                    i32 dx = x - center_x;
                    i32 dy = y - center_y;
                    i32 dist = (i32)sqrtf((f32)(dx * dx + dy * dy));
                    if (dist < radius) {
                        pxl8_2d_pixel(gfx, x, y, transition->color);
                    }
                }
            }
            break;
        }

        case PXL8_TRANSITION_DISSOLVE: {
            u32 seed = 12345;
            for (i32 y = 0; y < height; y++) {
                for (i32 x = 0; x < width; x++) {
                    seed = seed * 1103515245 + 12345;
                    f32 noise = (f32)((seed / 65536) % 1000) / 1000.0f;
                    if (noise < progress) {
                        pxl8_2d_pixel(gfx, x, y, transition->color);
                    }
                }
            }
            break;
        }

        case PXL8_TRANSITION_PIXELATE: {
            i32 max_block_size = 32;
            i32 block_size = (i32)(max_block_size * progress);
            if (block_size < 1) block_size = 1;

            pxl8_pixel_mode mode = pxl8_gfx_get_pixel_mode(gfx);
            bool has_fb = (mode == PXL8_PIXEL_HICOLOR)
                ? (pxl8_gfx_get_framebuffer_hicolor(gfx) != NULL)
                : (pxl8_gfx_get_framebuffer_indexed(gfx) != NULL);
            if (!has_fb) break;

            for (i32 y = 0; y < height; y += block_size) {
                for (i32 x = 0; x < width; x += block_size) {
                    u32 color_sum_r = 0, color_sum_g = 0, color_sum_b = 0;
                    i32 count = 0;

                    for (i32 by = 0; by < block_size && y + by < height; by++) {
                        for (i32 bx = 0; bx < block_size && x + bx < width; bx++) {
                            u32 color = pxl8_2d_get_pixel(gfx, x + bx, y + by);
                            color_sum_r += (color >> 16) & 0xFF;
                            color_sum_g += (color >> 8) & 0xFF;
                            color_sum_b += color & 0xFF;
                            count++;
                        }
                    }

                    if (count > 0) {
                        u32 avg_color = 0xFF000000 |
                                        ((color_sum_r / count) << 16) |
                                        ((color_sum_g / count) << 8) |
                                        (color_sum_b / count);

                        for (i32 by = 0; by < block_size && y + by < height; by++) {
                            for (i32 bx = 0; bx < block_size && x + bx < width; bx++) {
                                pxl8_2d_pixel(gfx, x + bx, y + by, avg_color);
                            }
                        }
                    }
                }
            }
            break;
        }
    }
}

void pxl8_transition_reset(pxl8_transition* transition) {
    if (!transition) return;
    transition->time = 0.0f;
    transition->active = false;
}

void pxl8_transition_set_color(pxl8_transition* transition, u32 color) {
    if (!transition) return;
    transition->color = color;
}

void pxl8_transition_set_reverse(pxl8_transition* transition, bool reverse) {
    if (!transition) return;
    transition->reverse = reverse;
}

void pxl8_transition_start(pxl8_transition* transition) {
    if (!transition) return;
    transition->active = true;
    transition->time = 0.0f;
}

void pxl8_transition_stop(pxl8_transition* transition) {
    if (!transition) return;
    transition->active = false;
}

void pxl8_transition_update(pxl8_transition* transition, f32 dt) {
    if (!transition || !transition->active) return;

    transition->time += dt;

    if (transition->time >= transition->duration) {
        transition->time = transition->duration;
        transition->active = false;

        if (transition->on_complete) {
            transition->on_complete(transition->userdata);
        }
    }
}
