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

#include <iostream>
#include <SDL2/SDL_image.h>
#include "Renderer.h"

#define RAD_TO_DEG 57.2957795130f

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

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

    // 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;
    }
}

bool Renderer::initialize(int nbPlayers) {
    // 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 = 1200;
    m_screenHeight = 700;

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

    // Opening window
    m_window = SDL_CreateWindow("< TinyShooter >",
                                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;
    }

    // Split-screen
    locateViews(nbPlayers);

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

    // Okay
    return loadEveryPicture();
}

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, int which) {
    // Rect
    SDL_Rect dst;
    SDL_Texture *tex;
    b2Vec2 rel;

    // View port (useful with split-screen)
    if (which < 0 || which > 3)
        return;
    SDL_RenderSetViewport(m_renderer, &m_viewPort[which]);

    SDL_SetRenderDrawColor(m_renderer, 0xFF, 0xFF, 0xFF, 0xFF);
    dst.x = 0;
    dst.y = 0;
    dst.w = m_viewPort[which].w;
    dst.h = m_viewPort[which].h;
    SDL_RenderFillRect(m_renderer, &dst);

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

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

        // Zoom correction
        dst.w = (int) (zoom * dst.w / DEFAULT_ZOOM);
        dst.h = (int) (zoom * 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::presentWindow() {
    // Activate display
    SDL_RenderPresent(m_renderer);
}

bool Renderer::loadPicture(std::string name) {
    SDL_Texture *texture = IMG_LoadTexture(m_renderer, name.c_str());

    if (texture == nullptr) {
        std::cout << "Renderer::loadPicture() > " << SDL_GetError() << std::endl << std::endl;
        return false;
    }

    m_pictureTab.push_back(texture);
    return true;
}

bool Renderer::loadEveryPicture() {
    bool okay(true);
    okay = okay && loadPicture("Pictures/NoPict.png");      // 0
    okay = okay && loadPicture("Pictures/Ally.png");        // 1
    okay = okay && loadPicture("Pictures/TinyWall1.png");   // 2
    okay = okay && loadPicture("Pictures/TinyWall2.png");   // 3
    okay = okay && loadPicture("Pictures/TinyWall3.png");   // 4
    okay = okay && loadPicture("Pictures/TinyWall4.png");   // 5
    okay = okay && loadPicture("Pictures/HighWall1.png");   // 6
    okay = okay && loadPicture("Pictures/HighWall2.png");   // 7
    okay = okay && loadPicture("Pictures/HighWall3.png");   // 8
    okay = okay && loadPicture("Pictures/HighWall4.png");   // 9
    okay = okay && loadPicture("Pictures/RedVisor.png");    // 10
    okay = okay && loadPicture("Pictures/RedArrow.png");    // 11
    okay = okay && loadPicture("Pictures/Bullet1.png");     // 12
    okay = okay && loadPicture("Pictures/Foe.png");         // 13
    okay = okay && loadPicture("Pictures/Bullet2.png");     // 14
    okay = okay && loadPicture("Pictures/LifeBar1.png");     // 15
    okay = okay && loadPicture("Pictures/LifeBar2.png");     // 16
    okay = okay && loadPicture("Pictures/LifeBar3.png");     // 17
    okay = okay && loadPicture("Pictures/LifeBar4.png");     // 18
    okay = okay && loadPicture("Pictures/LifeBar5.png");     // 19
    okay = okay && loadPicture("Pictures/LifeBar6.png");     // 20
    okay = okay && loadPicture("Pictures/WideWall1.png");     // 21
    okay = okay && loadPicture("Pictures/WideWall2.png");     // 22
    okay = okay && loadPicture("Pictures/WideWall3.png");     // 23
    okay = okay && loadPicture("Pictures/WideWall4.png");     // 24
    okay = okay && loadPicture("Pictures/BigWall1.png");     // 25
    okay = okay && loadPicture("Pictures/BigWall2.png");     // 26
    okay = okay && loadPicture("Pictures/BigWall3.png");     // 27
    okay = okay && loadPicture("Pictures/BigWall4.png");     // 28

    return okay;
}

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

void Renderer::locateViews(int nbPlayers) {
    switch (nbPlayers) {
        default: // One player
            // First and single screen
            m_viewPort[0].x = 0;
            m_viewPort[0].y = 0;
            m_viewPort[0].w = m_screenWidth;
            m_viewPort[0].h = m_screenHeight;
            break;
        case 2: // Two players
            // First screen
            m_viewPort[0].x = 0;
            m_viewPort[0].y = 0;
            m_viewPort[0].w = m_screenWidth / 2 - m_border;
            m_viewPort[0].h = m_screenHeight;

            // Second screen
            m_viewPort[1].x = m_screenWidth / 2 + m_border;
            m_viewPort[1].y = 0;
            m_viewPort[1].w = m_screenWidth / 2;
            m_viewPort[1].h = m_screenHeight;
            break;
        case 3: // Three players
            // First screen
            m_viewPort[0].x = 0;
            m_viewPort[0].y = 0;
            m_viewPort[0].w = m_screenWidth / 2 - m_border;
            m_viewPort[0].h = m_screenHeight / 2;

            // Second screen
            m_viewPort[1].x = m_screenWidth / 2 + m_border;
            m_viewPort[1].y = 0;
            m_viewPort[1].w = m_screenWidth / 2;
            m_viewPort[1].h = m_screenHeight / 2;

            // Third screen
            m_viewPort[2].x = m_screenWidth / 4;
            m_viewPort[2].y = m_screenHeight / 2 + m_border;
            m_viewPort[2].w = m_screenWidth / 2;
            m_viewPort[2].h = m_screenHeight / 2;
            break;
        case 4: // Four players
            // First screen
            m_viewPort[0].x = 0;
            m_viewPort[0].y = 0;
            m_viewPort[0].w = m_screenWidth / 2 - m_border;
            m_viewPort[0].h = m_screenHeight / 2;

            // Second screen
            m_viewPort[1].x = m_screenWidth / 2 + m_border;
            m_viewPort[1].y = 0;
            m_viewPort[1].w = m_screenWidth / 2;
            m_viewPort[1].h = m_screenHeight / 2;

            // Third screen
            m_viewPort[2].x = 0;
            m_viewPort[2].y = m_screenHeight / 2;
            m_viewPort[2].w = m_screenWidth / 2 - m_border;
            m_viewPort[2].h = m_screenHeight / 2;

            // Fourth screen
            m_viewPort[3].x = m_screenWidth / 2 + m_border;
            m_viewPort[3].y = m_screenHeight / 2;
            m_viewPort[3].w = m_screenWidth / 2;
            m_viewPort[3].h = m_screenHeight / 2;
            break;
    }
}

b2Vec2 Renderer::computeDiago(float zoom, int which) {
    // Test limits
    if (which < 0 || which > 3)
        return b2Vec2_zero;

    // Compute with view-port dims and zoom
    b2Vec2 rep;

    rep.x = m_viewPort[which].w / 2;
    rep.x /= zoom;

    rep.y = m_viewPort[which].h / 2;
    rep.y /= zoom;

    return rep;
}