#pragma once
#include "bx/timer.h"
#include <cstdint>

#define START_PERF() int64_t __perfStart = bx::getHPCounter();
#define END_PERF(counters, idx, frame) counters[(int32_t)idx].Write(bx::getHPCounter() - __perfStart, frame);

enum class PerfCounterType
{
    WindowEvents,
    GameDelta,
    GameLevelUpdate,
    Submit,
    COUNT
};

constexpr const char* PerfCounterNames[(int32_t)PerfCounterType::COUNT]{
    "WindowEvt",
    "Delta",
    "Level",
    "Submit",
};
struct PerfCounter
{
    static constexpr int32_t TimeWindow = 128;
    int64_t Times[TimeWindow]{0};
    void Write(int64_t value, uint64_t frameCounter)
    {
        Times[frameCounter % TimeWindow] = value;
    }
    float GetAverage()
    {
        int64_t sum = 0;
        for (int32_t i = 0; i < TimeWindow; ++i)
        {
            sum += Times[i];
        }
        return (double)sum / bx::getHPFrequency();
    }
    float GetMin()
    {
        int64_t min = INT64_MAX;
        for (int32_t i = 0; i < TimeWindow; ++i)
        {
            if (Times[i] < min)
            {
                min = Times[i];
            }
        }
        return (double)min / bx::getHPFrequency();
    }
    float GetMax()
    {
        int64_t max = 0;
        for (int32_t i = 0; i < TimeWindow; ++i)
        {
            if (Times[i] > max)
            {
                max = Times[i];
            }
        }
        return (double)max / bx::getHPFrequency();
    }
};

class SDL_Window;
union SDL_Event;

struct SharedWindowData
{
    SDL_Window* SDLWindow = nullptr;
    SDL_Event* SDLEvents{nullptr};
    uint16_t SDLEventCount = 0;
    void* Handle = nullptr;
    int32_t WindowWidth = 1920;
    int32_t WindowHeight = 1080;
    bool HeldScanCodes[512]{false};
    bool LastHeldScanCodes[512]{false};
    bool HeldMouseButtons[8]{false};
    bool LastHeldMouseButtons[8]{false};
    float MouseDeltaX = 0.0f;
    float MouseDeltaY = 0.0f;
    uint64_t FrameCounter = 0;
    PerfCounter PerfCounters[(int32_t)PerfCounterType::COUNT] = {0};
};

struct FileChangeNotification
{
    wchar_t FileName[128]{0};
};

struct SharedDevData
{
    uint32_t ChangedShaderCount = 0;
    FileChangeNotification ChangedShaders[16];
    char ShaderLog[2048]{0};
};

struct GameData
{
    void* PermanentStorage = nullptr;
    uint64_t PermanentStorageSize = 0;

    void* EntityStorage = nullptr;
    uint64_t EntityStorageSize = 0;

    void* TransientStorage = nullptr;
    uint64_t TransientStorageSize = 0;
};

struct SharedData
{
    GameData Game;
    SharedDevData Dev;
    SharedWindowData Window;
};

typedef void (*Startup)(SharedData& shared);
typedef void (*Update)();
typedef void (*Shutdown)();
