#include "Global.h"
#include "Input.h"
#include "Instance.h"
#include "Log.h"
#include "Setup.h"
#include "rendering/Rendering.h"

#include "bx/bx.h"
#include "bx/timer.h"
#ifdef TRACY_ENABLE
#include <client/TracyProfiler.hpp>
#endif
#include <tracy/Tracy.hpp>

namespace Game
{
    class GameSetup
    {
      public:
        GameRendering Rendering;
    };

    namespace
    {
        GameSetup SetupInstance;
    }

    void Setup(SharedData& shared)
    {
        LOG("Game Setup Start!");
#ifdef TRACY_ENABLE
        LOG("Tracy is enabled");
        tracy::StartupProfiler();
#endif

        if (shared.Game.PermanentStorage == nullptr)
        {
            LOG_ERROR("Game memory not initialized!!");
            return;
        }
        if (shared.Game.EntityStorage == nullptr)
        {
            LOG_ERROR("Entity memory not initialized!");
            return;
        }
        if (shared.Game.PermanentStorageSize < sizeof(GameInstance))
        {
            LOG_ERROR("Game memory too small! %u < %u", shared.Game.PermanentStorageSize, sizeof(GameInstance));
            return;
        }
        GameInstance& instance = *reinterpret_cast<GameInstance*>(shared.Game.PermanentStorage);
        if (sizeof(GameInstance) != instance.Size)
        {
            LOG_WARN("Game instance size changed, resetting!");
            instance = {};
        }
        instance.UsedScratchAmount = 0;
        SetShared(shared);
        SetInstance(instance);
        Puzzle::LoadStaticPuzzleData();
        SetupInstance.Rendering.Setup();
        instance.GameLevel.Setup(shared.Game);
        instance.IsInitialized = true;
    }

    void Update()
    {
        {
            ZoneScopedN("TimeUpdate");
            auto& inst = GetInstance();
            int64_t newNowHP = bx::getHPCounter() - inst.Time.StartTime;
            inst.Time.DeltaHP = newNowHP - inst.Time.NowHP;
            inst.Time.NowHP = newNowHP;
            inst.Time.Now = (double)inst.Time.NowHP / bx::getHPFrequency();
            inst.Time.Delta = (double)inst.Time.DeltaHP / bx::getHPFrequency();
            GetShared().Window.PerfCounters[(int32_t)PerfCounterType::GameDelta].Write(inst.Time.DeltaHP,
                                                                                       GetShared().Window.FrameCounter);
        }

        SetupInstance.Rendering.Update();

        {
            ZoneScopedN("MouseDeltaUpdate");
            auto& win = GetShared().Window;
            win.MouseDeltaX = 0.0f;
            win.MouseDeltaY = 0.0f;
            bx::memCopy(win.LastHeldScanCodes, win.HeldScanCodes, sizeof(win.HeldScanCodes));
            bx::memCopy(win.LastHeldMouseButtons, win.HeldMouseButtons, sizeof(win.HeldMouseButtons));
        }
        FrameMark;
    }

    void Shutdown()
    {
        LOG("Shutdown");
        SetupInstance.Rendering.Shutdown();
#ifdef TRACY_ENABLE
        tracy::ShutdownProfiler();
#endif
    }
} // namespace Game
