#pragma once

#include "../AliveLibCommon/FunctionFwd.hpp"
#include "DynamicArray.hpp"
#include "../AliveLibCommon/BitField.hpp"

ALIVE_VAR_EXTERN(u32, sGnFrame_5C1B84);

enum class AETypes : s16
{
    eNone_0 = 0,
    eAlarm_1 = 1,
    eSligSpawner_2 = 2,
    eScreenManager_6 = 6,
    eBackgroundAnimation_7 = 7,
    eLiftMover_9 = 9,
    eTimedMine_or_MovingBomb_10 = 10,
    eBone_11 = 11,
    eBoneBag_12 = 12,
    eBrewMachine_13 = 13,
    eBullet_15 = 15,
    eSligButton_16 = 16,
    eExplosionSet_18 = 18,
    eDDCheat_19 = 19,
    eSparks_22 = 22,
    eGasClock_23 = 23,
    eMetal_24 = 24,
    eMinesAlarm_25 = 25,
    eCrawlingSlig_26 = 26,
    eParticleBurst_29 = 29,
    eDrill_30 = 30,
    eLaughingGas_31 = 31,
    eDoor_33 = 33,
    eDoorLock_34 = 34,
    eDove_35 = 35,
    eGameSpeak_38 = 38,
    eElectricWall_39 = 39,
    ePalOverwriter_44 = 44,
    eEvilFart_45 = 45,
    eBaseBomb_46 = 46,
    eSecurityClaw_47 = 47,
    eRockSpawner_48 = 48,
    eFleech_50 = 50,
    ePossessionFlicker_51 = 51,
    eThrowableTotalIndicator_53 = 53,
    eFlyingSlig_54 = 54,
    eFlyingSligSpawner_55 = 55,
    eFootSwitch_56 = 56,
    eGameEnderController_57 = 57,
    eColourfulMeter_59 = 59,
    eSlapLock_OrbWhirlWind_60 = 60,
    eLockedSoul_61 = 61,
    eDeathBird_62 = 62,
    eGreeter_64 = 64,
    eGrenade_65 = 65,
    eGrenadeMachine_66 = 66,
    eGlukkon_67 = 67,
    eHelpPhone_68 = 68,
    eAbe_69 = 69,
    eEyeOrbPart_74 = 74,
    eInvisibleEffect_75 = 75,
    ePulley_76 = 76,
    eLiftPoint_78 = 78,
    eResourceManager_70 = 79,
    eSligGetPantsOrWings_80 = 80,
    eMudokon2_81 = 81,
    eSecurityOrb_83 = 83,
    eMeat_84 = 84,
    eMeatSack_85 = 85,
    eMeatSaw_86 = 86, // from AO
    eText_87 = 87,
    eMine_88 = 88,
    eMineCar_89 = 89,
    eGreeterBody_91 = 91,
    eMusicController_93 = 93,
    eMusicTrigger_94 = 94,
    ePauseMenu_95 = 95,
    eParamite_96 = 96,
    eDemoPlayback_98 = 98,
    eBirdPortal_99 = 99,
    eBirdPortalTerminator_100 = 100,
    eFG1_101 = 101,
    ePullRope_103 = 103,
    eAbilityRing_104 = 104,
    eRock_105 = 105,
    eRockSack_106 = 106,
    eLiftRope_108 = 108,
    eExplosion_109 = 109,
    eMudokon_110 = 110,
    eRedLaser_111 = 111,
    eScrab_112 = 112,
    eScrabSpawner_113 = 113,
    eScreenClipper_114 = 114,
    eEffectBase_115 = 115,
    eMainMenuTransistion_116 = 116,
    eFlash_117 = 117,
    eScreenShake_118 = 118,
    eShrykull_121 = 121,
    eSlamDoor_122 = 122,
    eSnoozeParticle_124 = 124,
    eSlig_125 = 125,
    eSlog_126 = 126,
    eSlurgSpawner_128 = 128,
    eSlurg_129 = 129,
    eParticle_134 = 134,
    eZapLine_135 = 135,
    eTimerTrigger_136 = 136,
    eLever_139 = 139,
    eTorturedMud_141 = 141,
    eTrapDoor_142 = 142,
    eUXB_143 = 143,
    eMovie_145 = 145,
    eWebLine_146 = 146,
    eWell_147 = 147,
    eWheel_148 = 148,
    eCameraSwapper_149 = 149,
    eElectrocute_150 = 150,

    eDebugHelper_1001 = 1001, // NOTE: Not part of the original game - a debug helper only
    eDebugConsole_1002 = 1002,
    eDebugPathRenderer_1003 = 1003,

    eNeverSet_107 = 107, // taking damage from something in AO? maybe bees, bats etc?
    eNeverSet_8 = 8,
    eNeverSet_40 = 40,
    eNevetSet_127 = 127,

    ePadding_137 = 137,
    ePadding_138 = 138,
    ePadding_140 = 140,
    ePadding_130 = 130,
    ePadding_131 = 131,
    ePadding_132 = 132,
    ePadding_133 = 133,
    ePadding_123 = 123,
    ePadding_119 = 119,
    ePadding_120 = 120,
    ePadding_102 = 102,
    ePadding_97 = 97,
    ePadding_82 = 82,
    ePadding_77 = 77,
    ePadding_70 = 70,
    ePadding_71 = 71,
    ePadding_72 = 72,
    ePadding_73 = 73,
    ePadding_58 = 58,
    ePadding_52 = 52,
    ePadding_63 = 63,
    ePadding_49 = 49,
    ePadding_41 = 41,
    ePadding_42 = 42,
    ePadding_43 = 43,
    ePadding_36 = 36,
    ePadding_37 = 37,
    ePadding_3 = 3,
    ePadding_4 = 4,
    ePadding_5 = 5,
    ePadding_20 = 20,
    ePadding_21 = 21,
    ePadding_27 = 27,
    ePadding_28 = 28,
    ePadding_14 = 14,
    ePadding_92 = 92,
    ePadding_90 = 90,
    ePadding_32 = 32,
    ePadding_17 = 17,
    ePadding_144 = 144
};

struct PrimHeader;

class BaseGameObject
{
public:
    enum Options
    {
        // bit 01 = 0x001 = BaseGameObject couldn't add to list / it unsets inverse of 0xF80A / 0b1111100000001010
        eListAddFailed_Bit1 = 0x001,

        // bit 02 = 0x002 = do update?
        eUpdatable_Bit2 = 0x002,

        // bit 03 = 0x004 = dead
        eDead_Bit3 = 0x004,

        // bit 04 = 0x008 = render
        eDrawable_Bit4 = 0x008,

        // bit 05 = 0x010 = set by BaseAnimatedWithPhysicsGameObject
        eIsBaseAnimatedWithPhysicsObj_Bit5 = 0x010,

        // bit 06 = 0x020 = set by BaseAliveGameObject
        eIsBaseAliveGameObject_Bit6 = 0x020,

        // bit 07 = 0x040 = set by ChantSuppressor::ctor_466350 / MovingBomb::ctor_46FD40 - explodable?
        eCanExplode_Bit7 = 0x040,

        // bit 08 = 0x080 = set by Uxb::ctor_4DE9A0 = pressable?
        eInteractive_Bit8 = 0x080,

        // bit 09 = 0x100 = ?
        eSurviveDeathReset_Bit9 = 0x100,

        // bit 10 = 0x200 = still update when the camera is changing
        eUpdateDuringCamSwap_Bit10 = 0x200,

        // bit 11 = 0x400 = can never be removed
        eCantKill_Bit11 = 0x400
    };

    // Order must match VTable
    virtual BaseGameObject* VDestructor(s32) = 0; // Not an actual dtor because the generated compiler code has the param to determine if heap allocated or not
    virtual void VUpdate();
    virtual void VRender(PrimHeader** pOrderingTable);
    virtual void VScreenChanged();
    virtual void VStopAudio();
    virtual s32 VGetSaveState(u8* pSaveBuffer);

private:
    EXPORT void ScreenChanged_4DC0A0();

protected:
    EXPORT u8** Add_Resource_4DC130(u32 type, s32 resourceID);
    EXPORT void BaseGameObject_ctor_4DBFA0(s16 bAddToObjectList, s16 resourceArraySize);
    EXPORT void BaseGameObject_dtor_4DBEC0();

    EXPORT static s32 CCSTD Find_Flags_4DC170(s32 objectId);

    // Helper to check if a timer has expired
    template <class T>
    static inline bool Expired(const T& value)
    {
        return static_cast<s32>(sGnFrame_5C1B84) > value;
    }

    template <class T>
    static s32 MakeTimer(const T value)
    {
        return static_cast<s32>(sGnFrame_5C1B84) + value;
    }

public:
    void SetUpdateDelay(s32 updateDelay)
    {
        field_1C_update_delay = updateDelay;
    }

    s32 UpdateDelay() const
    {
        return field_1C_update_delay;
    }

public:
    void SetType(AETypes type)
    {
        field_4_typeId = type;
    }

    AETypes Type() const
    {
        return field_4_typeId;
    }

private:
    AETypes field_4_typeId;
public:
    BitField16<Options> field_6_flags;
    s32 field_8_object_id;
    s32 field_C_objectId;
    DynamicArrayT<u8*> field_10_resources_array;
private:
    s32 field_1C_update_delay;
};
ALIVE_ASSERT_SIZEOF(BaseGameObject, 0x20);


ALIVE_VAR_EXTERN(DynamicArrayT<BaseGameObject>*, gBaseGameObject_list_BB47C4);
