#include <paradiso/rgba.hpp>
#include <paradiso/sprite.hpp>

#include <fstream>
#include <iostream>
#include <optional>
#include <unordered_map>

// STB image loading
#define STB_IMAGE_IMPLEMENTATION
#include "lib/stb_image.h"

#include "lib/image_loader.hpp"

std::unordered_map<std::string, paradiso::Bitmap> image_loader::image_cache =
    std::unordered_map<std::string, paradiso::Bitmap>();

paradiso::Bitmap image_loader::load(const std::string& filename) {
    if (image_cache.find(filename) == image_cache.end()) {
        std::string path = ASSET_PATH + filename;
        std::cout << "Loading " << path << std::endl;
        auto data = readBMP(path);
        image_cache[filename] = data;
    }
    return image_cache[filename];
}

paradiso::Bitmap image_loader::readBMP(std::string& filename) {
    // Load with stb_image
    stbi_set_flip_vertically_on_load(true); // flip y axis for OpenGL
    int width, height, channels;
    unsigned char* image =
        stbi_load(filename.c_str(), &width, &height, &channels, 4);
    int size = width * height;

    if (!image)
        throw std::runtime_error("Error loading image");

    // Convert to Vector of RGBA
    std::vector<paradiso::RGBA> rgba = std::vector<paradiso::RGBA>(size);
    for (int i = 0; i < size; i++) {
        // get rgba values
        int pos = i * 4;
        int r = image[pos + 0];
        int g = image[pos + 1];
        int b = image[pos + 2];
        int a = image[pos + 3];

        // bug in from_rgba. it expects bgra, not rgba
        auto val = paradiso::RGBA::from_rgba(b, g, r, a);
        rgba[i] = val;
    }

    // rotate image by 90 degrees CW
    std::vector<paradiso::RGBA> rotated = std::vector<paradiso::RGBA>(size);
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {

            int pos = y * width + x;
            int pos_rotated = x * height + (height - y - 1);
            rotated[pos_rotated] = rgba[pos];
        }
    }

    // free image
    stbi_image_free(image);

    return paradiso::Bitmap::from_data(
        paradiso::Size{static_cast<unsigned int>(height),
                       static_cast<unsigned int>(width)},
        rotated);
}