#pragma once
#include <cstdint>
#include "../../../templeware/utils/math/vector/vector.h"

#define MEM_PAD(size) char _pad##__LINE__[size]

template <typename T>
struct RepeatedPtrField_t {
    struct Rep_t {
        int nAllocatedSize;
        T* tElements[(std::numeric_limits<int>::max() - 2 * sizeof(int)) / sizeof(void*)];
    };
    void* pArena;
    int nCurrentSize;
    int nTotalSize;
    Rep_t* pRep;
};

class CBasePB {
public:
    MEM_PAD(0x8); // 0x0 VTABLE
    std::uint32_t nHasBits; // 0x8
    std::uint64_t nCachedBits; // 0xC
    void SetBits(std::uint64_t nBits) { nCachedBits |= nBits; }
};
static_assert(sizeof(CBasePB) == 0x18);

class CMsgQAngle : public CBasePB {
public:
    QAngle_t angValue; // 0x18
};
static_assert(sizeof(CMsgQAngle) == 0x28);

class CMsgVector : public CBasePB {
public:
    Vector_t vecValue; // 0x18
};
static_assert(sizeof(CMsgVector) == 0x28);

class CCSGOInterpolationInfoPB : public CBasePB {
public:
    float flFraction; // 0x18
    int nSrcTick; // 0x1C
    int nDstTick; // 0x20
};
static_assert(sizeof(CCSGOInterpolationInfoPB) == 0x28);

class CCSGOInputHistoryEntryPB : public CBasePB {
public:
    CMsgQAngle* pViewAngles; // 0x18
    CMsgVector* pShootPosition; // 0x20
    CMsgVector* pTargetHeadPositionCheck; // 0x28
    CMsgVector* pTargetAbsPositionCheck; // 0x30
    CMsgQAngle* pTargetAngPositionCheck; // 0x38
    CCSGOInterpolationInfoPB* cl_interp; // 0x40
    CCSGOInterpolationInfoPB* sv_interp0; // 0x48
    CCSGOInterpolationInfoPB* sv_interp1; // 0x50
    CCSGOInterpolationInfoPB* player_interp; // 0x58
    int nRenderTickCount; // 0x60
    float flRenderTickFraction; // 0x64
    int nPlayerTickCount; // 0x68
    float flPlayerTickFraction; // 0x6C
    int nFrameNumber; // 0x70
    int nTargetEntIndex; // 0x74
};
static_assert(sizeof(CCSGOInputHistoryEntryPB) == 0x78);

struct CInButtonState {
    MEM_PAD(0x8); // 0x0 VTABLE
    std::uint64_t nValue; // 0x8
    std::uint64_t nValueChanged; // 0x10
    std::uint64_t nValueScroll; // 0x18
};
static_assert(sizeof(CInButtonState) == 0x20);

class CBaseUserCmdPB : public CBasePB {
public:
    RepeatedPtrField_t<void*> subtickMovesField;
    std::string* strMoveCrc;
    CInButtonState* pInButtonState; // 0x20
    CMsgQAngle* pViewAngles; // 0x28
    std::int32_t nLegacyCommandNumber;
    std::int32_t nClientTick;
    float flForwardMove;
    float flSideMove;
    float flUpMove;
    std::int32_t nImpulse;
    std::int32_t nWeaponSelect;
    std::int32_t nRandomSeed;
    std::int32_t nMousedX;
    std::int32_t nMousedY;
    std::uint32_t nConsumedServerAngleChanges;
    std::int32_t nCmdFlags;
    std::uint32_t nPawnEntityHandle;
};
static_assert(sizeof(CBaseUserCmdPB) == 0x80);

class CCSGOUserCmdPB {
public:
    std::uint32_t nHasBits;
    std::uint64_t nCachedSize;
    RepeatedPtrField_t<CCSGOInputHistoryEntryPB> inputHistoryField;
    CBaseUserCmdPB* pBaseCmd;
    bool bLeftHandDesired;
    std::int32_t nAttack3StartHistoryIndex;
    std::int32_t nAttack1StartHistoryIndex;
    std::int32_t nAttack2StartHistoryIndex;
    void CheckAndSetBits(std::uint32_t nBits) {
        if (!(nHasBits & nBits))
            nHasBits |= nBits;
    }
};
static_assert(sizeof(CCSGOUserCmdPB) == 0x40);

class CUserCmd {
public:
    MEM_PAD(0x8); // 0x0 VTABLE
    MEM_PAD(0x10); // 0x8
    CCSGOUserCmdPB csgoUserCmd; // 0x18
    CInButtonState nButtons; // 0x58
    MEM_PAD(0x20); // 0x78

    CCSGOInputHistoryEntryPB* GetInputHistoryEntry(int nIndex) {
        if (nIndex >= csgoUserCmd.inputHistoryField.pRep->nAllocatedSize || nIndex >= csgoUserCmd.inputHistoryField.nCurrentSize)
            return nullptr;
        return csgoUserCmd.inputHistoryField.pRep->tElements[nIndex];
    }

    void SetSubTickAngle(const QAngle_t& angView) {
        for (int i = 0; i < this->csgoUserCmd.inputHistoryField.pRep->nAllocatedSize; i++) {
            CCSGOInputHistoryEntryPB* pInputEntry = this->GetInputHistoryEntry(i);
            if (!pInputEntry || !pInputEntry->pViewAngles)
                continue;
            pInputEntry->pViewAngles->angValue = angView;
            pInputEntry->SetBits(0x1U); // INPUT_HISTORY_BITS_VIEWANGLES
        }
    }
};
static_assert(sizeof(CUserCmd) == 0x98); 