//
// Created by jovian on 18/07/17.
//

#include <iostream>
#include "Renderer.h"


#define RAD_TO_DEG 57.2957795130f

Renderer::Renderer()
        : m_screenWidth(0), m_screenHeight(0), m_window(nullptr), m_renderer(nullptr), m_font(nullptr) {}

Renderer::~Renderer() {
    // Destroy textures
    while (!m_pictureTab.empty()) {
        if (m_pictureTab.back() != nullptr)
            SDL_DestroyTexture(m_pictureTab.back());
        m_pictureTab.pop_back();
    }

    // Destroy string textures
    for (auto it(m_stringTab.begin()); it != m_stringTab.end(); it++) {
        if (it->second != nullptr)
            SDL_DestroyTexture(it->second);

        it->second = nullptr;
    }
    m_stringTab.clear();

    // Destroy SDL renderer
    if (m_renderer != nullptr) {
        SDL_DestroyRenderer(m_renderer);
        m_renderer = nullptr;
    }

    // Destroy the beautiful window
    if (m_window != nullptr) {
        SDL_DestroyWindow(m_window);
        m_window = nullptr;
    }

    // Quit modules
    TTF_Quit();
    SDL_Quit();
}

bool Renderer::initialize() {
    // Announce
    std::cout << "Renderer::initialize() > ";

    // Already initialized
    if (m_window != nullptr) {
        std::cout << "Window already created." << std::endl << std::endl;
        return false;
    }

    // Default screen size
    m_screenWidth = WIN_W;
    m_screenHeight = WIN_H;

    // Init video
    if (SDL_Init(SDL_INIT_VIDEO) != 0) {
        std::cout << "SDL video failed : " << SDL_GetError() << std::endl << std::endl;
        return false;
    }

    // Init font
    if (TTF_Init() == -1) {
        std::cout << "TTF failed : " << TTF_GetError() << std::endl << std::endl;
        return false;
    }

    m_font = TTF_OpenFont("droid.ttf", 28);
    if (m_font == NULL) {
        std::cout << "Open droid.ttf failed : " << TTF_GetError() << std::endl << std::endl;
        return false;
    }

    // Opening window
    m_window = SDL_CreateWindow("MetaBalls visualizer - Jovian Hersemeule",
                                SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                m_screenWidth, m_screenHeight,
                                SDL_WINDOW_SHOWN /*| SDL_WINDOW_FULLSCREEN_DESKTOP*/);
    if (m_window == nullptr) {
        std::cout << "Window creation failed : " << SDL_GetError() << std::endl << std::endl;
        SDL_Quit();
        return false;
    }

    // Hardware physical screen size
    SDL_GetWindowSize(m_window, &m_screenWidth, &m_screenHeight);

    // Create renderer
    m_renderer = SDL_CreateRenderer(m_window, -1,
                                    SDL_RENDERER_ACCELERATED |
                                    SDL_RENDERER_PRESENTVSYNC);
    if (m_renderer == nullptr) {
        SDL_DestroyWindow(m_window);
        std::cout << "SDL Renderer creation failed : " << SDL_GetError() << std::endl << std::endl;
        SDL_Quit();
        return false;
    }

    // End
    std::cout << "Done, no error detected." << std::endl;

    // Okay
    return true;
}

void Renderer::clearWindow() {
    // Clean up buffer
    SDL_SetRenderDrawColor(m_renderer, 0x00, 0x00, 0x00, 0x00);
    SDL_RenderClear(m_renderer);
}

/*void Renderer::renderScene(std::vector<Visual *> &scope, const b2Vec2 &center, float zoom) {
    // Texture existence
    if (m_pictureTab.empty())
        return;

    // Rect
    SDL_Rect dst;
    SDL_Texture *tex;
    b2Vec2 rel;
    float localZoom;

    // For each
    for (auto it(scope.begin()); it != scope.end(); it++) {
        // Skip if no invalid texture
        if ((*it)->getImgId() > m_pictureTab.size() || m_pictureTab[(*it)->getImgId()] == nullptr)
            tex = m_pictureTab[0];
        else
            tex = m_pictureTab[(*it)->getImgId()];

        // Adjusting local zoom
        localZoom = zoom * (*it)->getScale();

        // Rect set up
        rel = (*it)->getPos() - center;
        dst.x = (int) (rel.x * zoom) + m_screenWidth / 2;
        dst.y = (int) (rel.y * zoom) + m_screenHeight / 2;
        SDL_QueryTexture(tex, NULL, NULL, &dst.w, &dst.h);

        // Zoom correction
        dst.w = (int) (localZoom * dst.w / DEFAULT_ZOOM);
        dst.h = (int) (localZoom * dst.h / DEFAULT_ZOOM);

        // Center texture
        dst.x -= dst.w / 2;
        dst.y -= dst.h / 2;

        // SDL rendering
        SDL_RenderCopyEx(m_renderer, tex, NULL, &dst, (*it)->getAngle() * RAD_TO_DEG, NULL, SDL_FLIP_NONE);
    }

}*/

/*void Renderer::renderPlanetHUD(PlanetDef def) {
    // Variable
    SDL_Rect dst;

    // Line under color
    const Uint8 uncl(0xaf);

    // Render rect
    dst.x = 10;
    dst.y = 10;
    int w = m_screenWidth / 2 - 2*dst.x + 24;
    int h = m_screenHeight * 2 / 3 - 2*dst.y;
    dst.w = w;
    dst.h = h;

    SDL_SetRenderDrawColor(m_renderer, 0x66, 0x66, 0x66, 0xff);
    SDL_RenderFillRect(m_renderer, &dst);

    SDL_SetRenderDrawColor(m_renderer, 0xff, 0xff, 0xff, 0xff);
    SDL_RenderDrawRect(m_renderer, &dst);
    SDL_RenderDrawLine(m_renderer, dst.x, dst.y + 37, dst.x + dst.w - 1, dst.y + 37);

    // Render planet name
    dst.x = 11;
    dst.y = 11;
    renderName(dst, def.name);

    // Column
    dst.x = 20;

    // Jumps
#define H_NEXT_BAR 32
#define H_OFFSET_BAR 10

    // People
    dst.y = 60;
    SDL_SetRenderDrawColor(m_renderer, uncl, uncl, uncl, 0xff);
    SDL_RenderDrawLine(m_renderer, dst.x, dst.y + H_OFFSET_BAR + 8, dst.x + w - 12, dst.y + H_OFFSET_BAR + 8);
    renderName(dst, "People");
    renderBar(dst.y + H_OFFSET_BAR, def.stock.get(PEOPLE));
}*/

void Renderer::renderName(SDL_Rect &dst, std::string name) {
    // Seek an existing name texture
    auto it(m_stringTab.find(name));

    if (it == m_stringTab.end()) {
        // Not loaded yet, let's fix this !
        if (!loadStringTexture(name))
            return;

        SDL_QueryTexture(m_stringTab[name], NULL, NULL, &dst.w, &dst.h);
        SDL_RenderCopy(m_renderer, m_stringTab[name], NULL, &dst);
    } else {
        // Faster plot
        SDL_QueryTexture(it->second, NULL, NULL, &dst.w, &dst.h);
        SDL_RenderCopy(m_renderer, it->second, NULL, &dst);
    }
}

void Renderer::renderBoolMatrix(bool mat[WIN_H][WIN_W]) {
    SDL_SetRenderDrawColor(m_renderer, 255, 255, 0, 255);

    std::vector<SDL_Point> points;

    for (int i(0); i < WIN_H; i++)
        for (int j(0); j < WIN_W; j++) {
            if (mat[i][j])
                //SDL_RenderDrawPoint(m_renderer, j, i);
                points.push_back({j, i});
        }

    SDL_RenderDrawPoints(m_renderer, points.data(), (int)points.size());
}


/*void Renderer::renderBar(int y, const int &value) {
    // Set position
    SDL_Rect dst;
    dst.x = 208;
    dst.y = y;
    dst.h = 16;

    // Set color and scale
    if (value == 0) {
        SDL_SetRenderDrawColor(m_renderer, 0x00, 0x00, 0x00, 0xff);
        dst.w = dst.h;
    }
    else if (value < 400) {
        SDL_SetRenderDrawColor(m_renderer, 0x00, 0xff, 0x00, 0xff);
        dst.w = value;
    }
    else if (value < 4000) {
        SDL_SetRenderDrawColor(m_renderer, 0xff, 0xff, 0x00, 0xff);
        dst.w = value / 10;
    }
    else if (value < 400000) {
        SDL_SetRenderDrawColor(m_renderer, 0xff, 0xaa, 0x00, 0xff);
        dst.w = value / 1000;
    }
    else if (value < 40000000) {
        SDL_SetRenderDrawColor(m_renderer, 0xff, 0x55, 0x00, 0xff);
        dst.w = value / 100000;
    }
    else {
        SDL_SetRenderDrawColor(m_renderer, 0xff, 0x00, 0x00, 0xff);
        dst.w = value / 10000000;
    }

    // Draw
    SDL_RenderFillRect(m_renderer, &dst);
    SDL_SetRenderDrawColor(m_renderer, 0xaf, 0xaf, 0xaf, 0xff);
    if (value == 0) {
        SDL_RenderDrawLine(m_renderer, dst.x, dst.y, dst.x + dst.w - 1, dst.y + dst.h - 1);
        SDL_RenderDrawLine(m_renderer, dst.x, dst.y + dst.h - 1, dst.x + dst.w - 1, dst.y);
    } else {
        dst.w = 400;
    }
    SDL_RenderDrawRect(m_renderer, &dst);
}*/

void Renderer::presentWindow() {
    // Activate display
    SDL_RenderPresent(m_renderer);
}

bool Renderer::loadStringTexture(std::string name) {
    // Render text surface
    //SDL_Surface* textSurface = TTF_RenderText_Solid(m_font, name.c_str(), {0, 255, 0, 255});
    SDL_Surface *textSurface = TTF_RenderText_Shaded(m_font, name.c_str(), {0x00, 0xff, 0x00, 0xff},
                                                     {0x66, 0x66, 0x66, 0xff});

    if (textSurface == NULL) {
        std::cout << "Renderer::loadStringTexture() [1] > " << TTF_GetError() << std::endl << std::endl;
        return false;
    }

    // Create texture from surface pixels
    SDL_Texture *texture(SDL_CreateTextureFromSurface(m_renderer, textSurface));
    SDL_FreeSurface(textSurface);

    if (texture == NULL) {
        std::cout << "Renderer::loadStringTexture() [2] > " << SDL_GetError() << std::endl << std::endl;
        return false;
    }

    m_stringTab[name] = texture;

    // All right
    return true;
}

SDL_Window *Renderer::getWindow() const {
    return m_window;
}