#include "core/rendering/sprite/dissolve_sprite.hpp"

#include <algorithm>  // Para min
#include <cstdint>    // Para uint32_t

#include "core/rendering/surface.hpp"  // Para Surface

// Hash 2D estable per a dithering (rank aleatori per posició de píxel)
static auto pixelRank(int col, int row) -> float {
    auto h = (static_cast<uint32_t>(col) * 2246822519U) ^ (static_cast<uint32_t>(row) * 2654435761U);
    h ^= (h >> 13);
    h *= 1274126177U;
    h ^= (h >> 16);
    return static_cast<float>(h & 0xFFFFU) / 65536.0F;
}

// Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori)
auto DissolveSprite::computePixelRank(int col, int row, int frame_h, DissolveDirection dir) -> float {
    const float RANDOM = pixelRank(col, row);
    if (dir == DissolveDirection::NONE || frame_h <= 0) {
        return RANDOM;
    }

    float y_factor = 0.0F;
    if (dir == DissolveDirection::DOWN) {
        y_factor = static_cast<float>(row) / static_cast<float>(frame_h);
    } else {
        y_factor = static_cast<float>(frame_h - 1 - row) / static_cast<float>(frame_h);
    }

    return (y_factor * 0.7F) + (RANDOM * 0.3F);
}

// Constructor per a surface directa (sense AnimationResource)
DissolveSprite::DissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
    : AnimatedSprite(std::move(surface), pos) {
    if (surface_) {
        const int W = static_cast<int>(surface_->getWidth());
        const int H = static_cast<int>(surface_->getHeight());
        surface_display_ = std::make_shared<Surface>(W, H);
        surface_display_->setTransparentColor(surface_->getTransparentColor());
        surface_display_->clear(surface_->getTransparentColor());
    }
}

// Constructor
DissolveSprite::DissolveSprite(const AnimationResource& data)
    : AnimatedSprite(data) {
    if (surface_) {
        const int W = static_cast<int>(surface_->getWidth());
        const int H = static_cast<int>(surface_->getHeight());
        surface_display_ = std::make_shared<Surface>(W, H);
        surface_display_->setTransparentColor(surface_->getTransparentColor());
        // Inicialitza tots els píxels com a transparents
        surface_display_->clear(surface_->getTransparentColor());
    }
}

// Reconstrueix la surface_display_ filtrant píxels per progress_
void DissolveSprite::rebuildDisplaySurface() {
    if (!surface_ || !surface_display_) {
        return;
    }

    const SDL_FRect CLIP = clip_;
    const int SX = static_cast<int>(CLIP.x);
    const int SY = static_cast<int>(CLIP.y);
    const int SW = static_cast<int>(CLIP.w);
    const int SH = static_cast<int>(CLIP.h);

    if (SW <= 0 || SH <= 0) {
        return;
    }

    auto src_data = surface_->getSurfaceData();
    auto dst_data = surface_display_->getSurfaceData();

    const int SRC_W = static_cast<int>(src_data->width);
    const int DST_W = static_cast<int>(dst_data->width);
    const Uint8 TRANSPARENT = surface_->getTransparentColor();

    // Esborra frame anterior si ha canviat
    if (prev_clip_.w > 0 && prev_clip_.h > 0 &&
        (prev_clip_.x != CLIP.x || prev_clip_.y != CLIP.y ||
            prev_clip_.w != CLIP.w || prev_clip_.h != CLIP.h)) {
        surface_display_->fillRect(&prev_clip_, TRANSPARENT);
    }

    // Esborra la zona del frame actual (reconstrucció neta)
    surface_display_->fillRect(&CLIP, TRANSPARENT);

    // Copia píxels filtrats per progress_
    for (int row = 0; row < SH; ++row) {
        for (int col = 0; col < SW; ++col) {
            const Uint8 COLOR = src_data->data[((SY + row) * SRC_W) + (SX + col)];
            if (COLOR == TRANSPARENT) {
                continue;
            }
            const float RANK = computePixelRank(col, row, SH, direction_);
            if (RANK >= progress_) {
                const Uint8 OUT = (COLOR == source_color_) ? target_color_ : COLOR;
                dst_data->data[((SY + row) * DST_W) + (SX + col)] = OUT;
            }
        }
    }

    prev_clip_ = CLIP;
    needs_rebuild_ = false;
}

// Actualitza animació, moviment i transició temporal
void DissolveSprite::update(float delta_time) {
    const SDL_FRect OLD_CLIP = clip_;
    AnimatedSprite::update(delta_time);

    // Detecta canvi de frame d'animació
    if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y ||
        clip_.w != OLD_CLIP.w || clip_.h != OLD_CLIP.h) {
        needs_rebuild_ = true;
    }

    // Actualitza transició temporal si activa
    if (transition_mode_ != TransitionMode::NONE) {
        transition_elapsed_ += delta_time * 1000.0F;
        const float T = std::min(transition_elapsed_ / transition_duration_, 1.0F);
        progress_ = (transition_mode_ == TransitionMode::DISSOLVING) ? T : (1.0F - T);
        needs_rebuild_ = true;
        if (T >= 1.0F) {
            transition_mode_ = TransitionMode::NONE;
        }
    }

    if (needs_rebuild_) {
        rebuildDisplaySurface();
    }
}

// Renderitza: usa surface_display_ (amb color replace) si disponible
void DissolveSprite::render() {
    if (!surface_display_) {
        AnimatedSprite::render();
        return;
    }
    surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
}

// Estableix el progrés manualment
void DissolveSprite::setProgress(float progress) {
    progress_ = std::min(std::max(progress, 0.0F), 1.0F);
    needs_rebuild_ = true;
}

// Inicia dissolució temporal (visible → invisible)
void DissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) {
    direction_ = dir;
    transition_mode_ = TransitionMode::DISSOLVING;
    transition_duration_ = duration_ms;
    transition_elapsed_ = 0.0F;
    progress_ = 0.0F;
    needs_rebuild_ = true;
}

// Inicia generació temporal (invisible → visible)
void DissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) {
    direction_ = dir;
    transition_mode_ = TransitionMode::GENERATING;
    transition_duration_ = duration_ms;
    transition_elapsed_ = 0.0F;
    progress_ = 1.0F;
    needs_rebuild_ = true;
}

// Atura la transició temporal
void DissolveSprite::stopTransition() {
    transition_mode_ = TransitionMode::NONE;
}

// Retorna si la transició ha acabat
auto DissolveSprite::isTransitionDone() const -> bool {
    return transition_mode_ == TransitionMode::NONE;
}

// Configura substitució de color per a la reconstrucció
void DissolveSprite::setColorReplace(Uint8 source, Uint8 target) {
    source_color_ = source;
    target_color_ = target;
    needs_rebuild_ = true;
}
