#ifndef D_SND_BGM_MML_PARSER_BASE_H
#define D_SND_BGM_MML_PARSER_BASE_H

#include "common.h"
#include "nw4r/snd/snd_SoundArchive.h"
#include "nw4r/snd/snd_SoundArchivePlayer.h"

// [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2cdd6
struct dBgmMmlCallStack
{
    bool	loopFlag;		// size 0x01, offset 0x00
    u8		loopCount;		// size 0x01, offset 0x01
    /* 2 bytes padding */
    byte_t	const *address;	// size 0x04, offset 0x04
}; // size 0x08

/**
 * Size: 0x28
 */
struct dBgmMmlTrack {
    /* 0x00 */ const byte_t *currentAddr;
    /* 0x04 */ u8 mTrackNo;
    /* 0x05 */ bool mTrackUsed;
    /* 0x06 */ u8 cmpFlag;
    /* 0x08 */ s32 wait;
    /* 0x0C */ dBgmMmlCallStack callStack[3];
    /* 0x24 */ s32 callStackDepth;
};

/**
 * @brief Parses harp key data
 *
 * Partial copy of nw4r::snd::detail::MmlParser
 */
class dSndBgmMmlParser_c {
protected:
    // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x2d70c
    // Swapped - or maybe just returns a boolean
    enum ParseResult {
        PARSE_RESULT_FINISH,
        PARSE_RESULT_CONTINUE,
    };

    // [R89JEL]:/bin/RVL/Debug/mainD.elf:.debug::0x31279
    enum SeqArgType {
        SEQ_ARG_NONE,

        SEQ_ARG_U8,
        SEQ_ARG_S16,
        SEQ_ARG_VMIDI,
        SEQ_ARG_RANDOM,
        SEQ_ARG_VARIABLE,
    };

    enum MmlCommand {
        MML_CMD_MIN = 0x80, // <80 -> MML note, not a command
        MML_CMD_MAX = 0xff,

        MML_CMD_MASK = 0x80,
        MML_CMD_SET_MASK = 0xf0,

        MML_WAIT = 0x80,
        MML_SET_PRGNO,

        MML_OPEN_TRACK = 0x88,
        MML_JUMP,
        MML_CALL,

        MML_ARG_1_RANDOM = 0xa0,
        MML_ARG_1_VARIABLE,
        MML_EXEC_IF,
        MML_ARG_2_S16,
        MML_ARG_2_RANDOM,
        MML_ARG_2_VARIABLE,

        MML_SET_TIMEBASE = 0xb0,
        MML_SET_ENV_HOLD,
        MML_SET_MONOPHONIC,
        MML_SET_TRACK_VELOCITY_RANGE,
        MML_SET_BIQUAD_TYPE,
        MML_SET_BIQUAD_VALUE,

        MML_SET_PAN = 0xc0,
        MML_SET_TRACK_VOLUME,
        MML_SET_PLAYER_VOLUME,
        MML_SET_TRANSPOSE,
        MML_SET_PITCH_BEND,
        MML_SET_BEND_RANGE,
        MML_SET_PRIORITY,
        MML_SET_NOTE_WAIT,
        MML_SET_TIE,
        MML_SET_PORTAMENTO,
        MML_SET_LFO_DEPTH,
        MML_SET_LFO_SPEED,
        MML_SET_LFO_TARGET,
        MML_SET_LFO_RANGE,
        MML_SET_PORTASPEED,
        MML_SET_PORTATIME,

        MML_SET_ATTACK = 0xd0,
        MML_SET_DECAY,
        MML_SET_SUSTAIN,
        MML_SET_RELEASE,
        MML_LOOP_START,
        MML_SET_TRACK_VOLUME2,
        MML_PRINT_VAR,
        MML_SET_SURROUND_PAN,
        MML_SET_LPF_FREQ,
        MML_SET_FX_SEND_A,
        MML_SET_FX_SEND_B,
        MML_SET_MAIN_SEND,
        MML_SET_INIT_PAN,
        MML_SET_MUTE,
        MML_SET_FX_SEND_C,
        MML_SET_DAMPER,

        MML_SET_LFO_DELAY = 0xe0,
        MML_SET_TEMPO,
        MML_SET_E2,
        MML_SET_SWEEP_PITCH,

        MML_RESET_ADSR = 0xfb,
        MML_LOOP_END,
        MML_RET,
        MML_ALLOC_TRACK,
        MML_EOF
    };

    enum MmlExCommand {
        MML_EX_COMMAND = 0xf0,

        MML_EX_CMD_MAX = 0xffff,

        MML_EX_ARITHMETIC = 0x80,
        MML_EX_SET = 0x80,
        MML_EX_APL,
        MML_EX_AMI,
        MML_EX_AMU,
        MML_EX_ADV,
        MML_EX_ALS,
        MML_EX_RND,
        MML_EX_AAD,
        MML_EX_AOR,
        MML_EX_AER,
        MML_EX_ACO,
        MML_EX_AMD,

        MML_EX_LOGIC = 0x90,
        MML_EX_EQ = 0x90,
        MML_EX_GE,
        MML_EX_GT,
        MML_EX_LE,
        MML_EX_LT,
        MML_EX_NE,

        MML_EX_USERPROC = 0xe0,
    };

public:
    dSndBgmMmlParser_c(nw4r::snd::SoundArchivePlayer *player, nw4r::snd::SoundArchive *archive);

    virtual void CommandProc(u32 trackNo, int wait, u32 command, s32 commandArg1, s32 commandArg2) const;
    virtual void NoteOnCommandProc(u32 trackNo, int wait, int key, int velocity, s32 length) const;

protected:
    bool loadAndParse(u32 soundId, u32 trackMask, bool noJumps);

private:
    bool readTracks(u32 soundId, u32 trackMask, bool noJumps);
    void initTrack(s32 trackNo, u32 offset);
    ParseResult Parse(dBgmMmlTrack *track) const;
    void CommandProc_(dBgmMmlTrack *track, u32 command, s32 commandArg1, s32 commandArg2) const;

    u8 ReadByte(byte_t const **ptr) const {
        return *(*ptr)++;
    }
    u16 Read16(byte_t const **ptr) const;
    u32 Read24(byte_t const **ptr) const;
    s32 ReadVar(byte_t const **ptr) const;
    s32 ReadArg(byte_t const **ptr, SeqArgType argType) const;

    // static members
public:
    static int const TEMPO_MAX = 1023;
    static int const TEMPO_MIN = 0;
    static int const CALL_STACK_DEPTH = 3;
    static int const SURROUND_PAN_CENTER;
    static int const PAN_CENTER = 64;

private:
    static bool mPrintVarEnabledFlag;

    // members
private:
    /* vtable */ // size 0x04, offset 0x00
    /* 0x04 */ u8 field_0x04;
    /* 0x08 */ nw4r::snd::SoundArchivePlayer *mpPlayer;
    /* 0x0C */ nw4r::snd::SoundArchive *mpArchive;
    /* 0x10 */ const byte_t *baseAddr;
    /* 0x14 */ dBgmMmlTrack mTracks[16];
}; // size 0x04

#endif
