#pragma once
/*
 * Created by Brett on 28/11/23.
 * Licensed under GNU General Public License V3.0
 * See LICENSE file for license detail
 */

#ifndef BLT_WITH_GRAPHICS_TEMPLATE_WINDOW_H
#define BLT_WITH_GRAPHICS_TEMPLATE_WINDOW_H

#include <blt/gfx/gl_includes.h>
#include <blt/std/types.h>
#include <GLFW/glfw3.h>
#include <functional>
#include <cstdint>
#include <string>
#include <utility>

namespace blt::gfx
{
    struct window_context
    {
        i32 GL_MAJOR = 4;
        i32 GL_MINOR = 6;
        i32 DOUBLE_BUFFER = GLFW_TRUE;
        i32 GL_PROFILE = GLFW_OPENGL_CORE_PROFILE;
        i32 SAMPLES = 4;
    };
    
    struct window_data
    {
        private:
            std::function<void(const window_data&)> init;
            std::function<void(const window_data&)> update;
            std::function<void(const window_data&)> destroy;
        public:
            std::string title;
            std::int32_t width;
            std::int32_t height;
            GLFWmonitor* monitor = nullptr;
            GLFWwindow* share = nullptr;
            GLFWwindow* window = nullptr;
            std::int32_t maximized = GLFW_FALSE;
            
            window_context context{};
            std::int32_t sync_interval = 0;
            
            window_data(std::string_view title, std::function<void(const window_data&)> init, std::function<void(const window_data&)> update,
                        std::function<void(const window_data&)> destroy, i32 width = 640, i32 height = 480):
                    init(std::move(init)), update(std::move(update)), destroy(std::move(destroy)),
                    title(title), width(width), height(height)
            {}
            
            inline void call_init() const
            {
                init(*this);
            }
            
            inline void call_update() const
            {
                update(*this);
            }
            
            inline void call_destroy() const
            {
                destroy(*this);
            }
            
            window_data& setWindowSize(int32_t new_width, int32_t new_height);
            
            window_data& setHeight(int32_t new_height)
            {
                setWindowSize(width, new_height);
                return *this;
            }
            
            window_data& setWidth(int32_t new_width)
            {
                setWindowSize(new_width, height);
                return *this;
            }
            
            window_data& setTitle(const std::string& title_str)
            {
                window_data::title = title_str;
                return *this;
            }
            
            window_data& setContext(const window_context& ctx)
            {
                context = ctx;
                return *this;
            }
            
            window_data& setSyncInterval(std::int32_t sync)
            {
                sync_interval = sync;
                return *this;
            }
            
            window_data& setMonitor(GLFWmonitor* monitor);
            
            window_data& setShare(GLFWwindow* share);
            
            window_data& setMaximized(bool b);
    };
    
    void init(window_data data);
    
    double getMouseX();
    
    double getMouseY();
    
    double getMouseDX();
    
    double getMouseDY();
    
    bool mouseMovedLastFrame();
    
    void lockCursor();
    
    void unlockCursor();
    
    bool isCursorLocked();
    
    bool isCursorInWindow();
    
    void setRawInput(bool state);
    
    bool isRawInput();
    
    void setClipboard(const std::string& str);
    
    std::string getClipboard();
    
    bool isMousePressed(int button);
    
    bool mousePressedLastFrame();
    
    bool mouseReleaseLastFrame();
    
    bool isKeyPressed(int key);
    
    // TODO: maybe make this per key?
    bool keyPressedLastFrame();
    
    bool keyReleasedLastFrame();
    
    double getFrameDeltaSeconds();
    
    double getFrameDeltaMilliseconds();
    
    i64 getFrameDelta();
    
    i32 getWindowWidth();
    
    i32 getWindowHeight();

    void setWindowSize(i32 width, i32 height);

    void cleanup();
    
    
}

#endif //BLT_WITH_GRAPHICS_TEMPLATE_WINDOW_H
