#pragma once

#include "Message/BMGEnums.hpp"
#include "System/SysNew.hpp"
#include "global.h"
#include "types.h"

#define BMG_MAGIC "MESGbmg1"
#define BMG_GET_INF1(pGroups, flags) ((pGroups)->entries[(flags) >> 0x10].func_02037258((flags) & 0xFFFF))
#define BMG_GET_MSG_OFFSET(pGroups, flags) (BMG_GET_INF1((pGroups), (flags))->offset)
#define BMG_GET_MSG_ADDR(pGroups, flags)                                                              \
    ((u32) (pGroups)->entries[(flags) >> 0x10].pDAT1 + (BMG_GET_MSG_OFFSET((pGroups), (flags)) & ~1))

#define BMG_ID(group, infIndex) (((group) << 16) | (infIndex))
#define BMG_ID_NONE 0

enum BMGGroup_ {
    BMGGroup_regular       = 0x00,
    BMGGroup_select        = 0x02,
    BMGGroup_maingame      = 0x03,
    BMGGroup_battle_parent = 0x04,
    BMGGroup_battle_common = 0x05,
    BMGGroup_field         = 0x07,
    BMGGroup_dungeon       = 0x08,
    BMGGroup_train         = 0x09,
    BMGGroup_demo          = 0x0A,
    BMGGroup_castle        = 0x0B,
    BMGGroup_castle_town   = 0x0C,
    BMGGroup_tower         = 0x0D,
    BMGGroup_tower_lobby   = 0x0E,
    BMGGroup_forest        = 0x0F,
    BMGGroup_snow          = 0x10,
    BMGGroup_water         = 0x11,
    BMGGroup_flame         = 0x12,
    BMGGroup_intrain       = 0x13,
    BMGGroup_village       = 0x14,
    BMGGroup_collect       = 0x15,
    BMGGroup_demo01_05     = 0x16,
    BMGGroup_demo06_10     = 0x17,
    BMGGroup_demo11_15     = 0x18,
    BMGGroup_demo16_20     = 0x19,
    BMGGroup_demo21_25     = 0x1A,
    BMGGroup_shop          = 0x1B,
    BMGGroup_flame_fld     = 0x1C,
    BMGGroup_post          = 0x1D,
    BMGGroup_desert        = 0x1E,
    BMGGroup_staff         = 0x1F,
    BMGGroup_train_extra   = 0x20,
};

enum BMGTag {
    /* "INF1" */ BMG_TAG_INF1 = '1FNI',
    /* "FLW1" */ BMG_TAG_FLW1 = '1WLF',
    /* "FLI1" */ BMG_TAG_FLI1 = '1ILF',
    /* "DAT1" */ BMG_TAG_DAT1 = '1TAD',
    /* "MID1" */ BMG_TAG_MID1 = '1DIM',
    /* "STR1" */ BMG_TAG_STR1 = '1RTS',
};

enum BMGEncoding {
    /* 1 */ BMG_ENCODING_CP1252 = 1,
    /* 2 */ BMG_ENCODING_UTF16_BE,
    /* 3 */ BMG_ENCODING_SHIFT_JIS,
    /* 4 */ BMG_ENCODING_UTF8,
    /* 5 */ BMG_ENCODING_MAX
};

//! TODO: find if this is still a thing
typedef enum BMGFileIndex {
    /*  0 */ BMG_FILE_INDEX_0,
    /* 34 */ BMG_FILE_INDEX_MAX
} BMGFileIndex;

struct SectionBase {
    /* 00 */ u32 tag;  // "INF1", "DAT1", ...
    /* 04 */ u32 size; // the size of the section
    /* 08 */
};

struct BMGHeader {
    /* 00 */ char magic[8];   // always "MESGbmg1"
    /* 08 */ u32 fileSize;    // the size of the BMG file
    /* 0C */ u32 numSections; // the number of sections (INF1, DAT1, ...)
    /* 10 */ u8 encoding;     // see `BMGEncoding`
    /* 11 */ u8 unk_11[0x0F]; // alignment padding?
    /* 20 */
};

struct EntryINF1 {
    /* 00 */ u32 offset; // relative to the end of the DAT1 header
    /* 04 */ u8 mUnk_04; // flags/attributes? (+0x04 to +0x06)
    /* 05 */ u8 mUnk_05;
    /* 06 */ u8 fontIndex; // see FontIndex enum
    /* 07 */ s8 mUnk_07;
    /* 08 */
};

struct SectionINF1 {
    /* 00 */ SectionBase base;
    /* 08 */ u16 numEntries;
    /* 0A */ u16 entrySize;
    /* 0C */ u16 groupId;
    /* 0E */ u8 colorId;
    /* 0F */ u8 mUnk_0F[0x1]; // alignment padding?
    /* 10 */ EntryINF1 *entries;
    /* 14 */
};

enum InstrType {
    /* 1 */ FLW1_TYPE_SHOW_MSG = 1,
    /* 2 */ FLW1_TYPE_BRANCH   = 2,
    /* 3 */ FLW1_TYPE_EVENT    = 3,
    /* 4 */
};

struct InstrShowMsg {
    /* 01 */ u8 bmgFileIndex;      // index into sBMGFiles
    /* 02 */ u16 msgIndex;         // index of INF1 entry
    /* 04 */ s16 nextIndex;        // index of FLW1 entry, 0xFFFF stops the conversation
    /* 06 */ s16 nextBMGFileIndex; // index into sBMGFiles
    /* 08 */
};

struct InstrBranch {
    /* 01 */ u8 mUnk_01;
    /* 02 */ u16 funcIndex; // index of the query function to run
    /* 04 */ u16 funcArg;   // the argument to use in the function
    /* 06 */ u16 flwEntry;  // the index of the second section table to be used next in the conversation.
    /* 08 */
};

struct InstrEvent {
    /* 01 */ u8 funcIndex; // index of the query function to run
    /* 02 */ u16 flwEntry; // the index of the second section table to be used next in the conversation.
    /* 04 */ u32 funcArg;  // the argument to use in the function
    /* 08 */
};

struct FLW1Instr {
    /* 00 */ u8 type; // see InstrType
    /* 01 */ union {
        InstrShowMsg showMsg;
        InstrBranch branch;
        InstrEvent event;
    };
    /* 08 */
};

struct SectionFLW1 {
    /* 00 */ SectionBase base;
    /* 04 */ u16 numInstructions;
    /* 08 */ u16 numLabels;
    /* 0C */ u32 mUnk_0C; // always zero?
    /* 10 */ FLW1Instr *instructions;
    /* 14 */ s16 *flwEntries;
    /* 18 */ s8 *bmgFileIndices;
    /* 1C */
};

struct EntryFLI1 {
    /* 00 */ u32 msgFlowID;
    /* 04 */ u32 msgFlowNodeIndex;
    /* 08 */
};

struct SectionFLI1 {
    /* 00 */ SectionBase base;
    /* 04 */ u16 numEntries;
    /* 08 */ u16 entrySize;
    /* 0C */ u32 mUnk_0C; // always zero?
    /* 10 */ EntryFLI1 *entries;
    /* 14 */
};

struct EntryDAT1 {
    /* 00 */ char *text;
    /* 04 */
};

struct SectionDAT1 {
    /* 00 */ SectionBase base;
    /* 08 */ EntryDAT1 *entries;
    /* 0C */
};

struct BMGFileInfo {
    /* 00 */ BMGHeader *pHeader; // pointer to the file's header
    /* 04 */ SectionINF1 *pINF1; // pointer to the data informations (INF -> informations)
    /* 08 */ SectionFLW1 *pFLW1; // pointer to the message flow data (FLW -> flow)
    /* 0C */ SectionFLI1 *pFLI1; // pointer to the message flow index table (FLI -> flow index table)
    /* 10 */ SectionDAT1 *pDAT1; // pointer to the data (DAT -> data)
    /* 14 */ BMGHeader *mUnk_14; // same as pHeader (?)
    /* 18 */ s16 mUnk_18;        // stores `func_020372F0`->param3 value (currently undetermined purpose)
    /* 1A */ s16 groupId;        // stores the group id
    /* 1C */
};

class BMGGroups {
public:
    /* 00 */ BMGFileInfo *entries; // accessed with `groupId`
    /* 04 */ s32 numEntries;
    /* 08 */

    BMGGroups();
    ~BMGGroups();
};
