#ifndef D_SND_PLAYER_MGR_H
#define D_SND_PLAYER_MGR_H

#include "d/snd/d_snd_mgr.h"
#include "d/snd/d_snd_types.h"
#include "d/snd/d_snd_util.h"
#include "nw4r/snd/snd_MemorySoundArchive.h"
#include "nw4r/snd/snd_SoundArchivePlayer.h"
#include "nw4r/snd/snd_SoundHandle.h"
#include "nw4r/snd/snd_SoundStartable.h"

class dAcBase_c;

SND_DISPOSER_FORWARD_DECL(dSndPlayerMgr_c);

/**
 * The main interface for managing sound stuff. Will delegate to dSndMgr_c for
 * most things, but handles demo (cutscene) sound effects (SE_DEMO*) by itself.
 *
 * Previous Ghidra name: BgmMgr
 */
class dSndPlayerMgr_c {
public:
    SND_DISPOSER_MEMBERS(dSndPlayerMgr_c);

public:
    dSndPlayerMgr_c();

    void shutdown(s32 frames);
    bool isShutdown();
    void reset(s32 frames);
    bool isReset();
    void recoverReset();

    void setupRmtSpeaker(s32 channel);
    void shutdownRmtSpeaker(s32 channel);

    void clearTempStates();

    void setup();
    void shutdownDemo();
    void calc();
    void calcActive();

    void enterMenu();
    void leaveMenu();
    void enterMap();
    void leaveMap();
    void enterHelp();
    void leaveHelp();

    void enterHbm();
    void leaveHbm();

    bool fn_8035E000();
    void fn_8035E1B0(u16);
    bool fn_8035E220();
    void fn_8035E250(u16);
    bool fn_8035E2E0();
    void fn_8035E310();
    void enterSystemMenu();
    void leaveSystemMenu();

    void enterCaution();
    void leaveCaution();

    void setMsgActor(s32 msgIdx, dAcBase_c *actor);
    void unsetMsgActor();

    void enterMsgWait();
    void leaveMsgWait();

    void setupState0();
    void popToState0();
    void saveState1();
    void popToState1();
    void saveState2();
    void popToState2();

    bool loadGroup(u32 groupId);
    bool loadFileForSound(u32 soundId);
    bool isLoadedFileForSound(u32 soundId);
    bool isLoadedFileForBank(u32 bankId);
    bool isLoadedBnkSeForGroup(dSndSourceGroup_c *group);
    bool isLoadedFileAndWaveForSound(u32 soundId);
    bool isLoadedSeFileAndWaveForGroup(dSndSourceGroup_c *group);

    u32 getFreeSize();
    bool loadDemoArchive(const char *demoArchiveName);
    const char *getSoundArchivePath();
    u32 convertLabelStringToSoundId(const char *label) const;
    static bool isBgmPlayerId(u32 id);
    static bool isSoundPlayedByBgmBattlePlayer(u32 soundId);
    u32 getSomeUserParam(u32 soundId) const;

    nw4r::snd::SoundArchivePlayer &getSoundArchivePlayerForType(s32 sourceType);
    bool shouldUseDemoPlayer(s32 sourceType) const;

    u32 getRemoConSoundVariantDemo(u32 soundId) const;
    u32 getRemoConSoundVariant(u32 soundId) const;

    enum PlayerMgrFlag_e {
        MGR_HBM = 0x1,
        MGR_SHUTDOWN_RESET = 0x2,
        MGR_PAUSE = 0x4,
        MGR_MAP = 0x8,
        MGR_HELP = 0x10,
        MGR_SYSTEM = 0x20,
        MGR_MSG_WAIT = 0x40,

        MGR_CAUTION = 0x80,
    };

    bool isInit() const {
        return mState0 > 0;
    }

    bool checkFlag(u32 mask) const {
        return mFlags & mask;
    }

    void onFlag(u32 mask) {
        mFlags |= mask;
    }

    void offFlag(u32 mask) {
        mFlags &= ~mask;
    }

    const nw4r::snd::SoundArchive *getDemoArchive() const {
        if (mDemoSoundArchivePlayer.IsAvailable()) {
            return &mDemoSoundArchive;
        } else {
            return dSndMgr_c::GetInstance()->getArchive();
        }
    }

    const nw4r::snd::SoundArchive *getDemoArchiveDirectly() const {
        return &mDemoSoundArchive;
    }

    u32 getEventMuteMask(u32 id) {
        return getDemoArchive()->GetSoundUserParam(id) & sEventMuteFlagsMask;
    }

    enum FanfareUnmuteParam_e {
        FANFARE_UNMUTE_BGM = 0x10000,
        FANFARE_UNMUTE_STAGE_EFFECTS = 0x20000,
        FANFARE_UNMUTE_ENEMY = 0x40000,
        FANFARE_UNMUTE_OBJECTS = 0x80000,
    };

    // seem to be used for Small and Event sounds
    enum UserParamEventSmall_e {
        MUTE_BGM_PART = 0x10000,
        MUTE_BGM_FULL = 0x20000,
        MUTE_STAGE_EFFECTS_PART = 0x40000,
        MUTE_STAGE_EFFECTS_FULL = 0x80000,
        MUTE_ENEMY_PART = 0x100000,
        MUTE_ENEMY_FULL = 0x200000,
        MUTE_OBJECTS_PART = 0x400000,
        MUTE_OBJECTS_FULL = 0x800000,
    };

    static const u32 sEventMuteFlagsMask;

    // For a full overview over which sounds are played by which players,
    // see https://gist.github.com/robojumper/f81a55857cebed2c79cf3cf3c1ec8f04
    enum PlayerIndex_e {
        /** Background music */
        PLAYER_BGM = 0,
        /** Background music, battles */
        PLAYER_BGM_BATTLE = 1,
        /** "fanfare?" */
        PLAYER_FAN = 2,
        /** small sound effects, only a few (UI navigation related) */
        PLAYER_SMALL_IMPORTANT = 3,
        /** small sound, often UI related */
        PLAYER_SMALL_NORMAL = 4,
        /** hit, roll attack, cloth, sink */
        PLAYER_LINK_BODY = 5,
        /** Link voice */
        PLAYER_LINK_VOICE = 6,
        /** Link footstep, swimming */
        PLAYER_LINK_FOOTSTEP = 7,
        /** Sword, beetle, ... */
        PLAYER_LINK_EQUIPMENT = 8,
        /** Enemy sound effects */
        PLAYER_ENEMY = 9,
        /** Enemy voices */
        PLAYER_ENEMY_VOICE = 10,
        /** Enemy footstep */
        PLAYER_ENEMY_FOOTSTEP = 11,
        /** Misc objects */
        PLAYER_OBJECT_1 = 12,
        /** Misc objects */
        PLAYER_OBJECT_2 = 13,
        /** NPC footsteps */
        PLAYER_NPC_FOOTSTEP = 14,
        /** NPC voices */
        PLAYER_NPC_VOICE = 15,
        /** leaves, grass */
        PLAYER_FOLIAGE = 16,
        /** misc TgSound environmental effects */
        PLAYER_TG_SOUND = 17,
        /** area sound effects (Faron birds...) */
        PLAYER_AREA = 18,
        /** not sure, only SE_A_IN_WATER_LV */
        PLAYER_AREA_IN_WATER_LV = 19,
        /** event */
        PLAYER_EVENT = 20,
        /** not sure */
        PLAYER_UNK_21 = 21,
    };

private:
    /* 0x010 */ u8 field_0x010;
    /* 0x011 */ bool mIsSetup;
    /* 0x014 */ s32 mState0;
    /* 0x018 */ s32 mState1;
    /* 0x01C */ s32 mState2;
    /* 0x020 */ u32 mFlags;

    void stopAllSound(s32 frames);
    void fn_8035E0E0();

    // system menu, inventory, map
    void enterPauseState();
    void leavePauseState();

    void initialize();
    void createFileManager();

public:
    virtual nw4r::snd::SoundStartable::StartResult
    startSound(nw4r::snd::SoundHandle *pHandle, u32 soundId, const nw4r::snd::SoundStartable::StartInfo *pStartInfo);
    virtual nw4r::snd::SoundStartable::StartResult startSound(
        nw4r::snd::SoundHandle *pHandle, const char *soundLabel, const nw4r::snd::SoundStartable::StartInfo *pStartInfo
    );

    nw4r::snd::SoundStartable::StartResult startDemoSound(
        nw4r::snd::SoundHandle *pHandle, u32 soundId, const nw4r::snd::SoundStartable::StartInfo *pStartInfo
    );

private:
    /* 0x028 */ nw4r::snd::MemorySoundArchive mDemoSoundArchive;
    /* 0x178 */ nw4r::snd::SoundArchivePlayer mDemoSoundArchivePlayer;
};

#endif
