#include <platform/window.h>
#include <platform/input.h>
#include <platform/sdl2.h>
#include <SDL.h>

namespace extra2d {

WindowSvc& WindowSvc::inst() {
    static WindowSvc instance;
    return instance;
}

bool WindowSvc::init() {
    if (isInited()) return true;
    
    if (!Sdl2::initVideo()) {
        return false;
    }
    
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    
    state_ = SvcState::Inited;
    return true;
}

void WindowSvc::shutdown() {
    if (glCtx_) {
        SDL_GL_DeleteContext(glCtx_);
        glCtx_ = nullptr;
    }
    if (window_) {
        SDL_DestroyWindow(window_);
        window_ = nullptr;
    }
    state_ = SvcState::Shutdown;
}

bool WindowSvc::create(const WindowCfg& cfg) {
    if (window_) return true;
    
    uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
    if (cfg.resizable) {
        flags |= SDL_WINDOW_RESIZABLE;
    }
    if (cfg.fullscreen) {
        flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
    }
    
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor);
    
    window_ = SDL_CreateWindow(
        cfg.title.c_str(),
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        cfg.width,
        cfg.height,
        flags
    );
    
    if (!window_) {
        return false;
    }
    
    glCtx_ = SDL_GL_CreateContext(window_);
    if (!glCtx_) {
        SDL_DestroyWindow(window_);
        window_ = nullptr;
        return false;
    }
    
    setVsync(cfg.vsync);
    vsync_ = cfg.vsync;
    
    return true;
}

bool WindowSvc::pollEvents() {
    SDL_Event evt;
    while (SDL_PollEvent(&evt)) {
        switch (evt.type) {
            case SDL_QUIT:
                shouldClose_ = true;
                if (onClose_) onClose_();
                break;
            case SDL_WINDOWEVENT:
                if (evt.window.event == SDL_WINDOWEVENT_RESIZED) {
                    if (onResize_) {
                        onResize_(evt.window.data1, evt.window.data2);
                    }
                }
                break;
            default:
                if (INPUT_SVC.isInited()) {
                    INPUT_SVC.processEvent(evt);
                }
                break;
        }
    }
    return !shouldClose_;
}

void WindowSvc::swapBuffers() {
    if (window_ && glCtx_) {
        SDL_GL_SwapWindow(window_);
    }
}

Size WindowSvc::getSize() const {
    if (!window_) return Size(0, 0);
    int w, h;
    SDL_GetWindowSize(window_, &w, &h);
    return Size(w, h);
}

Vec2 WindowSvc::getPosition() const {
    if (!window_) return Vec2(0, 0);
    int x, y;
    SDL_GetWindowPosition(window_, &x, &y);
    return Vec2(static_cast<float>(x), static_cast<float>(y));
}

void WindowSvc::setSize(int32 w, int32 h) {
    if (window_) {
        SDL_SetWindowSize(window_, w, h);
    }
}

void WindowSvc::setTitle(const std::string& title) {
    if (window_) {
        SDL_SetWindowTitle(window_, title.c_str());
    }
}

void WindowSvc::setFullscreen(bool fullscreen) {
    if (window_) {
        SDL_SetWindowFullscreen(window_, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
    }
}

bool WindowSvc::isFullscreen() const {
    if (!window_) return false;
    uint32 flags = SDL_GetWindowFlags(window_);
    return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
}

void WindowSvc::setVsync(bool vsync) {
    SDL_GL_SetSwapInterval(vsync ? 1 : 0);
    vsync_ = vsync;
}

bool WindowSvc::isVsync() const {
    return vsync_;
}

void WindowSvc::setVisible(bool visible) {
    if (window_) {
        if (visible) {
            SDL_ShowWindow(window_);
        } else {
            SDL_HideWindow(window_);
        }
    }
}

bool WindowSvc::isVisible() const {
    if (!window_) return false;
    uint32 flags = SDL_GetWindowFlags(window_);
    return (flags & SDL_WINDOW_SHOWN) != 0;
}

} // namespace extra2d
