#ifndef D_LYT_MAP_H
#define D_LYT_MAP_H

#include "common.h"
#include "d/d_cursor_hit_check.h"
#include "d/d_message.h"
#include "d/d_sc_game.h"
#include "d/lyt/d2d.h"
#include "d/lyt/d_lyt_map_capture.h"
#include "d/lyt/d_lyt_map_global.h"
#include "d/lyt/d_lyt_map_markers.h"
#include "d/lyt/d_textbox.h"
#include "d/lyt/d_window.h"
#include "egg/core/eggColorFader.h"
#include "egg/core/eggFader.h"
#include "m/m2d.h"
#include "m/m_allocator.h"
#include "m/m_angle.h"
#include "m/m_vec.h"
#include "nw4r/lyt/lyt_bounding.h"
#include "nw4r/lyt/lyt_group.h"
#include "nw4r/lyt/lyt_pane.h"
#include "s/s_State.hpp"
#include "toBeSorted/d_flow_mgr.h"

// TODO - this file uses a lot of `friend` classes because the necessary
// inlines might mess up weak function order. Let's get the order right
// first before introducing more problems...

class dAcTbox_c;

/** Bird Statue Definition (StatueSelectDestination) */
struct dMapSaveObjDefinition {
    /* 0x00 */ const char *stageName;
    /* 0x04 */ u8 room;
    /* 0x05 */ u8 layer;
    /* 0x06 */ u8 entrance;
    /* 0x08 */ s32 type;
    /* 0x0C */ const char *statueLabel;
};

/** Something for making sure re-opening the map opens it in a similar state as when it was last closed */
struct dMapSavedDataEntry {
    /* 0x00 */ s32 mapMode;
    /* 0x04 */ bool mapUpDirection;
    /* 0x05 */ bool mapUpDirectionAfterZoomToDetail;
    /* 0x06 */ bool isValid;
};

struct dMapSavedData {
    /* 0x00 */ dMapSavedDataEntry entries[/* dLytMapMain_c::ROOMTYPE_MAX */ 6];
    /* 0x30 */ bool showIslandNames;
};

struct dMapFootPrintEntry {
    /* 0x00 */ dMapFootPrintEntry *pPrev;
    /* 0x04 */ dMapFootPrintEntry *pNext;
    /* 0x08 */ mVec3_c position;
    /* 0x14 */ mAng rotation;
};

/** Generic footprints queue */
class dMapFootPrintsQueue_c {
public:
    dMapFootPrintsQueue_c() {
        init();
    }
    void init();
    void addStep(const mVec3_c &pos, const mAng &rot);
    dMapFootPrintEntry *getFirst() const;
    dMapFootPrintEntry *getNext(const dMapFootPrintEntry *it) const;

    bool isEmpty() const {
        return mCount == 0;
    }

private:
    /* 0x000 */ dMapFootPrintEntry mEntries[40];
    /* 0x3C0 */ dMapFootPrintEntry *mpFirst;
    /* 0x3C4 */ dMapFootPrintEntry *mpLast;
    /* 0x3C8 */ u32 mCount;
};

/** Records Link's footsteps */
class dMapFootPrintsMgr_c {
public:
    dMapFootPrintsMgr_c() : mMinStepDistanceSq(0.0f) {}
    virtual ~dMapFootPrintsMgr_c() {}

    void execute();

    void reset() {
        mQueue.init();
    }

    const dMapFootPrintsQueue_c *getQueue() const {
        return &mQueue;
    }

    void setFootstepMinDistSq(f32 value) {
        mMinStepDistanceSq = value;
    }

private:
    /* 0x004 */ dMapFootPrintsQueue_c mQueue;
    /* 0x3D0 */ f32 mMinStepDistanceSq;
};

/** 2D UI - Map - Link footprint markers */
class dLytMapFootPrints_c {
public:
    dLytMapFootPrints_c(dLytMapGlobal_c *global);
    virtual ~dLytMapFootPrints_c() {}

    bool build(d2d::ResAccIf_c *resAcc);
    bool remove();
    bool execute();
    void draw();

    void setVisible(bool visible) {
        mVisible = visible;
    }

    void setField_0x198(f32 f) {
        field_0x198 = f;
    }

    void setField_0x19C(f32 f) {
        field_0x19C = f;
    }

private:
    dLytMapGlobal_c *getGlobal() const;

    /* 0x004 */ dLytMapGlobal_c *mpGlobal;
    /* 0x008 */ d2d::dLytSub mLyt;
    /* 0x09C */ nw4r::lyt::Pane *mpPane;
    /* 0x0A0 */ f32 field_0xA0;
    /* 0x0A4 */ mVec2_c mFootprintPositions[30];
    /* 0x194 */ u16 mMaxNumSteps;
    /* 0x196 */ u16 mCurrentNumSteps;
    /* 0x198 */ f32 field_0x198;
    /* 0x19C */ f32 field_0x19C;
    /* 0x1A0 */ bool mVisible;
};

struct LytMap0x80520B5C {
    LytMap0x80520B5C() : field_0x04(false), field_0x05(false) {}
    virtual ~LytMap0x80520B5C() {}

    bool field_0x04;
    bool field_0x05;
};

/** 2D UI - Map - beacon preview icon following the cursor */
class dLytMapPutIcon_c {
public:
    dLytMapPutIcon_c() : mVisible(false), field_0x98(0.0f, 0.0f) {}
    virtual ~dLytMapPutIcon_c() {}

    void build(d2d::ResAccIf_c *resAcc);
    void remove();
    void execute();
    void draw();

    void setPosition(const mVec2_c &pos);
    void setScale(f32 scale);

    void setVisible(bool visible) {
        mVisible = visible;
    }

private:
    /* 0x04 */ d2d::LytBase_c mLyt;
    /* 0x94 */ bool mVisible;
    /* 0x98 */ mVec2_c field_0x98;
};

struct dLytMapFloorBtnAnmGroups {
    /* 0x00 */ d2d::AnmGroup_c mLoop;
    /* 0x40 */ d2d::AnmGroup_c mOnOff;
    /* 0x80 */ d2d::AnmGroup_c mDecide;
    /* 0xC0 */ d2d::AnmGroup_c mOnOffLight;
};

struct dLytMapBoundingWorldBounds {
    /* 0x00 */ mVec3_c min;
    /* 0x0C */ mVec3_c max;
};

/** 2D UI - Map - a placed down beacon */
class dLytMapPinIcon_c {
public:
    friend class dLytMapPinIconAggregate_c;

    dLytMapPinIcon_c()
        : mStateMgr(*this),
          mpBounding(nullptr),
          field_0x1BC(0),
          mPosition(0.0f, 0.0f, 0.0f),
          mIsSet(false),
          field_0x1D0(nullptr),
          field_0x1D4(0),
          mIndex(0),
          mLoopFrame(0) {}
    virtual ~dLytMapPinIcon_c();

    bool build(d2d::ResAccIf_c *resAcc);
    bool remove();
    bool draw();
    bool execute();

    void updatePosition();
    void checkPointedAt();
    void setPosition(const mVec3_c &pos);
    void setScale(f32 scale);

    bool isSelect() const {
        return *mStateMgr.getStateID() == dLytMapPinIcon_c::StateID_ToSelect ||
             *mStateMgr.getStateID() == dLytMapPinIcon_c::StateID_Select;
    }

    bool isRemove() const {
        return *mStateMgr.getStateID() == dLytMapPinIcon_c::StateID_Remove;
    }

    STATE_FUNC_DECLARE(dLytMapPinIcon_c, Wait);
    STATE_FUNC_DECLARE(dLytMapPinIcon_c, ToSelect);
    STATE_FUNC_DECLARE(dLytMapPinIcon_c, Select);
    STATE_FUNC_DECLARE(dLytMapPinIcon_c, ToUnselect);
    STATE_FUNC_DECLARE(dLytMapPinIcon_c, Remove);

private:
    void removeBeacon();

    dLytMapGlobal_c *getGlobal() const;

    /* 0x004 */ UI_STATE_MGR_DECLARE(dLytMapPinIcon_c);
    /* 0x040 */ d2d::LytBase_c mLyt;
    /* 0x0D0 */ d2d::AnmGroup_c mAnmGroups[3];
    /* 0x190 */ dCursorHitCheckLyt_c mCsHitCheck;
    /* 0x1B8 */ nw4r::lyt::Bounding *mpBounding;
    /* 0x1BC */ UNKWORD field_0x1BC;
    /* 0x1C0 */ mVec3_c mPosition;
    /* 0x1CC */ bool mIsSet;
    /* 0x1D0 */ LytMap0x80520B5C *field_0x1D0;
    /* 0x1D4 */ UNKWORD field_0x1D4;
    /* 0x1D8 */ s32 mIndex;
    /* 0x1DC */ UNKWORD mLoopFrame;
};

class dLytMapPinIconAggregate_c {
    friend class dLytMapMain_c;

public:
    dLytMapPinIconAggregate_c() : mStateMgr(*this) {
        field_0x9A0 = nullptr;
        mLoopFrame = 0;
        mLoopFrameMax = 0;
        for (int i = 0; i < 5; i++) {
            mPins[i].mIndex = i;
        }
    }
    virtual ~dLytMapPinIconAggregate_c() {}

    bool build(d2d::ResAccIf_c *resAcc);
    bool remove();
    bool execute();
    bool draw();

    void setScale(f32 scale);
    void updatePosition();
    void setUnk(LytMap0x80520B5C *unk);
    bool setPosition(s32 index, const mVec3_c &position);
    void unsetAll();
    u32 getNumSetPins() const;

    STATE_FUNC_DECLARE(dLytMapPinIconAggregate_c, Wait);
    STATE_FUNC_DECLARE(dLytMapPinIconAggregate_c, Select);
    STATE_FUNC_DECLARE(dLytMapPinIconAggregate_c, Remove);

private:
    bool findNewBeaconId(s32 *pOutId);

    /* 0x004 */ UI_STATE_MGR_DECLARE(dLytMapPinIconAggregate_c);
    /* 0x040 */ dLytMapPinIcon_c mPins[5];

    /* 0x9A0 */ LytMap0x80520B5C *field_0x9A0;
    /* 0x9A4 */ s32 mLoopFrame;
    /* 0x9A8 */ s32 mLoopFrameMax;
};

// Size 0x50
class dLytMapFloorBtn_c {
    friend class dLytMapFloorBtnMgr_c;

public:
    dLytMapFloorBtn_c()
        : mStateMgr(*this),
          mpOwnerLyt(nullptr),
          mpAnmGroups(nullptr),
          field_0x44(0),
          mpBounding(nullptr),
          mSelected(false),
          mDecided(false) {}
    ~dLytMapFloorBtn_c() {}

    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, Wait);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, ToSelect);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, Select);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, ToWait);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, ToDecide);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, Decide);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, ToSelectInDecide);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, SelectInDecide);
    STATE_FUNC_DECLARE(dLytMapFloorBtn_c, DecideToSelectInDecide);

    void init();
    void execute();

    bool build(d2d::ResAccIf_c *resAcc);

    void directlyDecide();
    void directlyUndecide();

private:
    /* 0x00 */ UI_STATE_MGR_DECLARE(dLytMapFloorBtn_c);
    /* 0x3C */ d2d::LytBase_c *mpOwnerLyt;
    /* 0x40 */ dLytMapFloorBtnAnmGroups *mpAnmGroups;
    /* 0x44 */ UNKWORD field_0x44;
    /* 0x48 */ nw4r::lyt::Bounding *mpBounding;
    /* 0x4C */ bool mSelected;
    /* 0x4D */ bool mDecided;
};

class dLytMapFloorBtnMgr_c : public d2d::dSubPane {
    friend class dLytMapMain_c;

public:
    dLytMapFloorBtnMgr_c(dLytMapGlobal_c *global)
        : mpGlobal(global), mpPane(nullptr), mStateMgr(*this) {
        mNumFloors = 1;
        mBaseFloorOffset = 0;
        mSelectedBtnIdx = 0;
        field_0x70C = 4;
        mPointerVisible = false;
        mPrevPointerVisible = false;
        mForceNoNav = false;
    }
    virtual ~dLytMapFloorBtnMgr_c();

    virtual bool build(d2d::ResAccIf_c *resAcc) override;
    virtual bool remove() override;
    virtual bool execute() override;
    virtual nw4r::lyt::Pane *getPane() override {
        return mLyt.getLayout()->GetRootPane();
    }
    virtual d2d::LytBase_c *getLyt() override {
        return &mLyt;
    }
    virtual const char *getName() const override {
        return mLyt.getName();
    }

    STATE_FUNC_DECLARE(dLytMapFloorBtnMgr_c, Invisible);
    STATE_FUNC_DECLARE(dLytMapFloorBtnMgr_c, Wait);

    void init(s32 numFloors, s32 baseFloorOffset, s32 selectedFloor);

    bool canDecideFloor() const;
    void decideSelectedFloor();
    s32 getCurrentFloor() const;
    s32 getPointedAtBtnIdx() const;

    bool hasPointedAtABtnIdx() const {
        return getPointedAtBtnIdx() >= 0;
    }

    void resetFloor(s32 newFloorBtn);
    bool isUsingPointerNav() const;

    void setForceNoNav(bool b) {
        mForceNoNav = b;
    }

    void checkPointedAtBtn();

private:
    /* 0x008 */ dLytMapGlobal_c *mpGlobal;
    /* 0x00C */ UI_STATE_MGR_DECLARE(dLytMapFloorBtnMgr_c);
    /* 0x048 */ d2d::dLytSub mLyt;
    /* 0x0DC */ d2d::AnmGroup_c mAnmGroups[1];
    /* 0x11C */ dLytMapFloorBtnAnmGroups mBtnGroups[4];
    /* 0x51C */ nw4r::lyt::Pane *mpPane;
    /* 0x520 */ dCursorHitCheckLyt_c mCsHitChecks[4];
    /* 0x5C0 */ dLytMapFloorBtn_c mFloorBtns[4];
    /* 0x700 */ s32 mNumFloors;
    /* 0x704 */ s32 mBaseFloorOffset;
    /* 0x708 */ s32 mSelectedBtnIdx;
    /* 0x70C */ UNKWORD field_0x70C;
    /* 0x710 */ bool mPointerVisible;
    /* 0x711 */ bool mPrevPointerVisible;
    /* 0x712 */ bool mForceNoNav;
};

// Made up name
class dLytMapFader_c : public m2d::Base_c {
public:
    dLytMapFader_c();
    virtual ~dLytMapFader_c();
    /* vt 0x0C */ virtual void draw() override;

    void calc();

    bool isVisible() const {
        return mIsVisible;
    }

    void setVisible(bool v) {
        mIsVisible = v;
    }

    void fadeOut() {
        mFader.setStatus(EGG::Fader::STATUS_PREPARE_OUT);
        mFader.setFrame(15);
        mFader.fadeOut();
    }

public:
    /* 0x10 */ EGG::ColorFader mFader;
    /* 0x34 */ u8 _0x34[0x38 - 0x34];
    /* 0x38 */ bool mIsVisible;
};

// Size 0x4C
class dLytMapPopupInfo_c {
    friend class dLytMapMain_c;

public:
    dLytMapPopupInfo_c() : mStateMgr(*this) {}

    STATE_FUNC_DECLARE(dLytMapPopupInfo_c, Invisible);
    STATE_FUNC_DECLARE(dLytMapPopupInfo_c, In);
    STATE_FUNC_DECLARE(dLytMapPopupInfo_c, Wait);
    STATE_FUNC_DECLARE(dLytMapPopupInfo_c, WaitInvalid);
    STATE_FUNC_DECLARE(dLytMapPopupInfo_c, Out);

    void build(d2d::ResAccIf_c *resAcc);
    void remove();
    void execute();
    void draw();

private:
    /* 0x000 */ UI_STATE_MGR_DECLARE(dLytMapPopupInfo_c);
    /* 0x03C */ d2d::LytBase_c mLyt;
    /* 0x0CC */ d2d::AnmGroup_c mAnmGroups[3];
};

// Size 0x4C
class dLytMapSavePopupAction_c {
    friend class dLytMapSavePopup_c;

public:
    dLytMapSavePopupAction_c() : mStateMgr(*this), mInOutFrame(0.0f), mInRequest(false) {}

    STATE_FUNC_DECLARE(dLytMapSavePopupAction_c, Invisible);
    STATE_FUNC_DECLARE(dLytMapSavePopupAction_c, In);
    STATE_FUNC_DECLARE(dLytMapSavePopupAction_c, Wait);
    STATE_FUNC_DECLARE(dLytMapSavePopupAction_c, Out);

    void execute();
    void init(d2d::AnmGroup_c *pGroups);
    void hide();

private:
    /* 0x00 */ UI_STATE_MGR_DECLARE(dLytMapSavePopupAction_c);
    /* 0x3C */ d2d::AnmGroup_c *mpAnmGroupInOut;
    /* 0x40 */ d2d::AnmGroup_c *mpAnmGroupInput;
    /* 0x44 */ f32 mInOutFrame;
    /* 0x48 */ bool mInRequest;
};

// Assumed name
class dLytMapSavePopup_c {
public:
    dLytMapSavePopup_c() : mStatueNum(0), mpStatueBoundings(nullptr), mCurrentlyInStatue(-1) {}

    void build(d2d::ResAccIf_c *resAcc);
    void remove();
    void execute();
    void draw();

    void set(nw4r::lyt::Bounding **pStatueBoundings, s32 count);
    void hide(s32 statueIdx);

    void setCurrentStatue(s32 idx) {
        mCurrentlyInStatue = idx;
    }

private:
    f32 setStatueLabel(const char *label);

    /* 0x000 */ d2d::LytBase_c mLyt;
    /* 0x090 */ d2d::AnmGroup_c mAnmGroups[2];
    /* 0x110 */ dLytMapSavePopupAction_c mActions[12];
    /* 0x4A0 */ s32 mStatueNum;
    /* 0x4A4 */ nw4r::lyt::Bounding **mpStatueBoundings;
    /* 0x4A8 */ s32 mCurrentlyInStatue;
};

class dLytMapSaveCaption_c {
    friend class dLytMapMain_c;

public:
    dLytMapSaveCaption_c() : mStateMgr(*this) {}

    STATE_FUNC_DECLARE(dLytMapSaveCaption_c, Invisible);
    STATE_FUNC_DECLARE(dLytMapSaveCaption_c, In);
    STATE_FUNC_DECLARE(dLytMapSaveCaption_c, Wait);
    STATE_FUNC_DECLARE(dLytMapSaveCaption_c, Out);

    void build(d2d::ResAccIf_c *resAcc);
    void remove();
    void execute();
    void draw();

    void setType(s32 type);
    void setLabel(const char *label);

private:
    /* 0x000 */ UI_STATE_MGR_DECLARE(dLytMapSaveCaption_c);
    /* 0x03C */ d2d::LytBase_c mLyt;
    /* 0x0CC */ d2d::AnmGroup_c mAnmGroups[5];
};

// Size 0x190
class dLytMapSaveObj_c {
    friend class dLytMapMain_c;

public:
    dLytMapSaveObj_c()
        : mStateMgr(*this), mSelectRequest(false), mDecideRequest(false), mDecideFinished(false) {}
    ~dLytMapSaveObj_c() {}

    STATE_FUNC_DECLARE(dLytMapSaveObj_c, Wait);
    STATE_FUNC_DECLARE(dLytMapSaveObj_c, ToSelect);
    STATE_FUNC_DECLARE(dLytMapSaveObj_c, Select);
    STATE_FUNC_DECLARE(dLytMapSaveObj_c, ToWait);
    STATE_FUNC_DECLARE(dLytMapSaveObj_c, Decide);

    void build(d2d::ResAccIf_c *resAcc);
    void remove();
    void execute();
    void draw();

    void setPosition(const mVec3_c &position);
    void setAlpha(u8 alpha);
    void init();

private:
    /* 0x000 */ UI_STATE_MGR_DECLARE(dLytMapSaveObj_c);
    /* 0x03C */ d2d::LytBase_c mLyt;
    /* 0x0CC */ d2d::AnmGroup_c mAnmGroups[3];
    /* 0x18C */ bool mSelectRequest;
    /* 0x18D */ bool mDecideRequest;
    /* 0x18E */ bool mDecideFinished;
};

class dLytMapMain_c : public m2d::Base_c {
    friend class dLytMap_c;

public:
    dLytMapMain_c();
    virtual ~dLytMapMain_c();
    virtual void draw() override;
    virtual void dLytMapMain_vt0x10();

    void build();
    void remove();
    void execute();

    bool checkClose() const;

    bool isOpen() const;
    bool canOpen() const;
    bool isVisible() const;
    void queueMapEvent(s32 mapEvent, s32 surfaceProvince, s32 c);
    void startMapEvent(s32 mapEvent, s32 surfaceProvince, s32 c);
    bool isVisibleNoIntro() const;
    void fn_8013B350();

    const dMapSaveObjDefinition *getSaveObjDefinition(s32 province, s32 statueIdx) const;

    STATE_FUNC_DECLARE(dLytMapMain_c, Invisible);
    STATE_FUNC_DECLARE(dLytMapMain_c, RenderingWait);
    STATE_FUNC_DECLARE(dLytMapMain_c, In);
    STATE_FUNC_DECLARE(dLytMapMain_c, Active);
    STATE_FUNC_DECLARE(dLytMapMain_c, Out);
    STATE_FUNC_DECLARE(dLytMapMain_c, FloorChange);
    STATE_FUNC_DECLARE(dLytMapMain_c, ChgMapUpDirection);
    STATE_FUNC_DECLARE(dLytMapMain_c, ChgDispAreaMode_Map);
    STATE_FUNC_DECLARE(dLytMapMain_c, ChgDispAreaMode_MapRot);
    STATE_FUNC_DECLARE(dLytMapMain_c, ChgDispAreaMode_WA);
    STATE_FUNC_DECLARE(dLytMapMain_c, ChgDispAreaMode_CsrRot);
    STATE_FUNC_DECLARE(dLytMapMain_c, ResetPos);
    STATE_FUNC_DECLARE(dLytMapMain_c, ResetPosWithFloorChange);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSwBankSmall_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSwBankSmall_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSwBankSmall_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventMapIntro_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventMapIntro_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventMapIntro_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventMapIntro_Step4);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventDungeonMapGet_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventDungeonMapGet_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventDungeonMapGet_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventFieldMapChange_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventFieldMapChange_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventFieldMapChange_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventFieldMapChange_Step4);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventForestMapChange_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventForestMapChange_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventForestMapChange_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSignalAdd_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSignalAdd_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSignalAdd_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventGoddessCube_Step1);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventGoddessCube_Step2);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventGoddessCube_Step3);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSaveObjMsgWindow);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSaveObjSelect);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSaveObjConfirmMsgWindow);
    STATE_FUNC_DECLARE(dLytMapMain_c, EventSaveObjDecide);

    enum MapEvent_e {
        MAP_EVENT_NONE = 0,
        MAP_EVENT_1 = 1,
        // ???
        MAP_EVENT_SW_BANK_SMALL = 2,
        MAP_EVENT_MAP_INTRO = 3,
        MAP_EVENT_DUNGEON_MAP_GET = 4,
        MAP_EVENT_FIELD_MAP_CHANGE_5 = 5,
        MAP_EVENT_FOREST_MAP_CHANGE = 6,
        MAP_EVENT_SIGNAL_ADD = 7,
        MAP_EVENT_FIELD_MAP_CHANGE_8 = 8,
        MAP_EVENT_GODDESS_CUBE = 9,
        /** arg1 = surface province */
        MAP_EVENT_SAVE_OBJ = 10,
        MAP_EVENT_11 = 11,

        MAP_EVENT_MAX = 12,
    };

    enum SurfaceProvince_e {
        SURFACE_PROVINCE_FARON = 0,
        SURFACE_PROVINCE_ELDIN = 1,
        SURFACE_PROVINCE_LANAYRU = 2,
    };

private:
    // TODO - need to come up with better names for all of these enums and concepts

    enum AreaGroup_e {
        AREAGROUP_SKY = 1,
        AREAGROUP_FARON = 2,
        AREAGROUP_ELDIN = 3,
        AREAGROUP_LANAYRU = 4,
        AREAGROUP_SEALED_GROUNDS = 5,
        AREAGROUP_MAX = 6,
    };

    enum RoomType_e {
        ROOMTYPE_FIELD = 0,
        ROOMTYPE_DUNGEON = 1,
        ROOMTYPE_SKYLOFT = 2,
        ROOMTYPE_BOSS_HOUSE = 3,
        ROOMTYPE_SKYFIELD = 4,
        ROOMTYPE_SKYFIELD_INTERIOR = 5,
        ROOMTYPE_MAX = 6,
    };

    dLytMapGlobal_c *getGlobal();
    void checkScroll();
    bool needsNav(s32 mapMode) const;
    bool canZoomOut(s32 mapMode) const;
    bool canZoomIn(s32 mapMode) const;
    bool canResetPosition(s32 mapMode, bool) const;
    bool canChangeUpDirection(s32 mapMode, bool) const;
    bool canCenterCursor(s32 mapMode) const;
    bool canCenterCursor1(s32 mapMode) const;
    bool canPlaceBeacons(s32 mapMode);
    bool canChangeFloor(s32 mapMode) const;
    bool isPointingAtMainMap() const;

    void loadStageProperties();
    s32 getAreaGroup(s32 stifArea) const;
    s32 getRoomType() const;

    s32 getSelectedSaveObjIdx() const;

    f32 fn_80142D90(s32);
    void fn_80142F00(mVec3_c &, s32 mapMode, bool, const mVec3_c &, const mAng &);
    void fn_8013FB70(const mVec3_c &, f32);
    bool fn_80141530() const;
    bool fn_80142D10(s32, bool, mAng &);
    void fn_80143060(mVec3_c &, const mVec3_c &, const mVec3_c &, const mAng &);
    void forceOut();
    void fn_80143300();
    void fn_80143120(s32);
    void fn_801431E0();
    void fn_8013AD50();
    void fn_80143360();
    void fn_80142C80(s32);
    bool isMapIntroDone() const;
    bool shouldDrawFootprints() const;

    void zoomIn();
    void zoomOut();

    void clearButtonMessages();
    void setButtonMessages(s32 currentMapMode, bool currentUpDirection, s32 nextMapMode, bool nextUpDirection);
    void setCursorType();

    void setupFlags();
    void setupStage();
    void setupEvent(s32 event, s32 arg1, s32 arg2);
    void close();

    void loadTextboxes();

    void saveMapState();
    static void initMapState();
    void loadMapState();

    void checkCursorPointedAtMap();

    void calculateBoundingWorldHitboxes();

    dAcTbox_c *findGoddessChestForStoryflag(s32 flag) const;
    bool checkStoryflag(s32 flag) const;
    void displaySaveObjs();
    bool isPaneVisible(nw4r::lyt::Pane *pane) const;
    void setSaveObjsVisible(bool visible);
    void setSaveObjPanePtrs();
    void initSaveObjs();

    u8 getRoomAlpha(s32 roomid) const;

    static dMapSavedData sSavedMapData;
    static const dMapSavedData sDefaultMapData;

    bool isMapEventEq0Or1Or7Or9Or11() const {
        return mMapEvent == MAP_EVENT_NONE || mMapEvent == MAP_EVENT_1 || mMapEvent == MAP_EVENT_SIGNAL_ADD ||
               mMapEvent == MAP_EVENT_GODDESS_CUBE || mMapEvent == MAP_EVENT_11;
    }

    bool isMapEventEq1Or7Or8Or9Or11() const {
        return mMapEvent == MAP_EVENT_1 || mMapEvent == MAP_EVENT_SIGNAL_ADD ||
               mMapEvent == MAP_EVENT_FIELD_MAP_CHANGE_8 || mMapEvent == MAP_EVENT_GODDESS_CUBE ||
               mMapEvent == MAP_EVENT_11;
    }

    void setMapEventDone(bool done) {
        mEventDone = done;
    }

    /* 0x0010 */ UI_STATE_MGR_DECLARE(dLytMapMain_c);
    /* 0x004C */ dFlowMgrBase_c mFlowMgr;
    /* 0x00A4 */ dFlow_c mFlow;
    /* 0x0108 */ UNKWORD field_0x0108;
    /* 0x010C */ d2d::LytBase_c mLyt;
    /* 0x019C */ d2d::AnmGroup_c mAnmGroups[54];
    /* 0x0F1C */ LytMap0x80520B5C field_0xF1C;
    /* 0x0F24 */ dLytMapCapture_c mMapCapture;
    /* 0x0FA0 */ dLytMapFloorBtnMgr_c mFloorBtnMgr;
    /* 0x16B4 */ dLytMapPinIconAggregate_c mPinIconAggregate;
    /* 0x2060 */ dLytMapMarkers_c mMarkers;
    /* 0x64C0 */ dLytMapFootPrints_c mFootPrints;
    /* 0x6664 */ dLytMapPutIcon_c mPutIcon;
    /* 0x6704 */ dLytMapSaveObj_c mSaveObjs[12];
    /* 0x79C4 */ dLytMapSaveCaption_c mSaveCaption;
    /* 0x7BD0 */ dLytMapSavePopup_c mSavePopup;
    /* 0x807C */ dLytMapPopupInfo_c mPopupInfo;
    /* 0x8208 */ dTextBox_c *mpNumberTextBox;
    /* 0x820C */ dTextBox_c *mpNumberTextBoxS;
    /* 0x8210 */ nw4r::lyt::Pane *mpNoroshiPane;
    /* 0x8214 */ nw4r::lyt::Pane *mpScaleFramePane;
    /* 0x8218 */ dWindow_c *mpWakuWindow;
    /* 0x821C */ nw4r::lyt::Bounding *mpSaveObjBoundingsFaron[10];
    /* 0x8244 */ nw4r::lyt::Bounding *mpSaveObjBoundingsEldin[6];
    /* 0x825C */ nw4r::lyt::Bounding *mpSaveObjBoundingsLanayru[12];
    /* 0x828C */ mVec3_c field_0x828C[12];
    /* 0x831C */ nw4r::lyt::Bounding **mpCurrentSaveObjBoundings;
    /* 0x8320 */ u32 mNumSaveObjs;

    /* 0x8324 */ u8 _0x8324[0x8328 - 0x8324];

    /* 0x8328 */ d2d::AnmGroup_c *mpInOutAnmGroup;

    // TODO - it appears the map abuses these hit check things
    // to calculate Lyt bounding boxes, and it stores the
    // results in 0x8948...
    /* 0x832C */ dCursorHitCheckLyt_c mHitChecks[33];

    /* 0x8854 */ nw4r::lyt::Pane *mpPanes[11];
    /* 0x8880 */ d2d::AnmGroup_c *field_0x8880[11]; // ???
    /* 0x88AC */ UNKWORD field_0x88AC;
    /* 0x88B0 */ nw4r::lyt::Pane *mpDungeonPanes[7];
    /* 0x88CC */ nw4r::lyt::Pane *mpDungeonPics[7];

    /* 0x88E8 */ nw4r::lyt::Pane *mpPaneBgAll01;
    /* 0x88EC */ nw4r::lyt::Pane *mpPaneBgAll02;
    /* 0x88F0 */ nw4r::lyt::Pane *mpPaneAll01;
    /* 0x88F4 */ nw4r::lyt::Pane *mpPaneAll02;
    /* 0x88F8 */ nw4r::lyt::Pane *mpPaneRotate00;
    /* 0x88FC */ nw4r::lyt::Pane *mpPaneRotate01;
    /* 0x8900 */ nw4r::lyt::Pane *mpAllPane;
    /* 0x8904 */ mVec3_c field_0x8904;
    /* 0x8910 */ mVec3_c field_0x8910;
    /* 0x891C */ mVec3_c field_0x891C;
    /* 0x8928 */ mVec2_c field_0x8928;
    /* 0x8930 */ mVec3_c field_0x8930;
    /* 0x893C */ mVec3_c field_0x893C;

    /* 0x8948 */ dLytMapBoundingWorldBounds mBoundingWorldBounds[33];

    /* 0x8C60 */ s32 mMaxBeaconCount;
    /* 0x8C64 */ s32 field_0x8C64;
    /* 0x8C68 */ s32 mRoomType;
    /* 0x8C6C */ s32 mAreaGroup;
    /* 0x8C70 */ u32 field_0x8C70;

    /* 0x8C74 */ u8 _0x8C74[0x8C7C - 0x8C74];
    /* 0x8C7C */ f32 field_0x8C7C;
    /* 0x8C80 */ f32 field_0x8C80;

    /* 0x8C84 */ u8 field_0x8C84;

    /* 0x8C88 */ f32 field_0x8C88;
    /* 0x8C8C */ f32 mOutDuration;
    /* 0x8C90 */ bool mMapUpDirection;
    /* 0x8C91 */ bool mNextMapUpDirection;
    /* 0x8C92 */ bool mMapUpDirectionAfterZoomToDetail;
    /* 0x8C93 */ u8 field_0x8C93;
    /* 0x8C94 */ s32 mMapEvent;
    /* 0x8C98 */ s32 mNextMapEvent;
    /* 0x8C9C */ s32 mMapEventArg1;
    /* 0x8CA0 */ s32 mMapEventArg2;

    /* 0x8CA4 */ s32 mCurrentMapMode;
    /* 0x8CA8 */ s32 mNextMapMode;

    /* 0x8CAC */ u8 field_0x8CAC;
    /* 0x8CAD */ bool mCameraTransitionPossible;
    /* 0x8CAE */ bool field_0x8CAE;

    /* 0x8CB0 */ u32 mEventTimer;
    /* 0x8CB4 */ UNKWORD field_0x8CB4;
    /* 0x8CB8 */ u32 field_0x8CB8;
    /* 0x8CBC */ nw4r::lyt::Bounding *mpMapBounding;
    /* 0x8CC0 */ bool mPointerOnMap;
    /* 0x8CC1 */ bool mPointerCanPlaceBeacon;
    /* 0x8CC4 */ mVec3_c field_0x8CC4;
    /* 0x8CD0 */ mVec3_c field_0x8CD0;
    /* 0x8CDC */ mVec3_c field_0x8CDC;
    /* 0x8CE8 */ mVec3_c field_0x8CE8;
    /* 0x8CF4 */ mVec3_c field_0x8CF4;
    /* 0x8D00 */ mVec3_c mMapScroll;
    /* 0x8D0C */ mVec3_c mPlayerPos;
    /* 0x8D18 */ mVec3_c field_0x8D18;
    /* 0x8D24 */ mVec3_c mGoddessChestWorldPosition;
    /* 0x8D30 */ mVec2_c field_0x8D30;
    /* 0x8D38 */ mVec2_c field_0x8D38;
    /* 0x8D40 */ f32 field_0x8D40;
    /* 0x8D44 */ mAng field_0x8D44;
    /* 0x8D46 */ mAng field_0x8D46;
    /* 0x8D48 */ mAng field_0x8D48;
    /* 0x8D4C */ f32 field_0x8D4C;
    /* 0x8D50 */ f32 field_0x8D50;
    /* 0x8D54 */ s32 mNumTitleLines;
    /* 0x8D58 */ s32 mCurrentFloor;
    /* 0x8D5C */ UNKWORD mNumFloors;
    /* 0x8D60 */ s32 mBaseFloorOffset;
    /* 0x8D64 */ s32 field_0x8D64;
    /* 0x8D68 */ mAng field_0x8D68;
    /* 0x8D6A */ bool mEventCanceled; // set at 0x8009e2d4
    /* 0x8D6B */ bool mEventDone;
    /* 0x8D6C */ nw4r::lyt::Pane *mpRegionPane1;
    /* 0x8D70 */ nw4r::lyt::Pane *mpRegionPane2;
    /* 0x8D74 */ nw4r::lyt::Pane *mpZoomInOutPane;
    /* 0x8D78 */ f32 field_0x8D78;
    /* 0x8D7C */ f32 field_0x8D7C;
    /* 0x8D80 */ f32 field_0x8D80;
    /* 0x8D84 */ f32 field_0x8D84;
    /* 0x8D88 */ f32 field_0x8D88;
    /* 0x8D8C */ nw4r::lyt::Group *mpPriorityGroups[2];
    /* 0x8D94 */ d2d::SubPaneList mSubpaneList;
    /* 0x8DA0 */ d2d::SubPaneListNode mSubpane;
    /* 0x8DB0 */ UNKWORD field_0x8DB0;
    /* 0x8DB4 */ bool mNavEnabled;
    /* 0x8DB5 */ bool mDrawScaleFrame;
    /* 0x8DB8 */ s32 mDisplayedBeaconCount;
    /* 0x8DBC */ bool mShowIslandNames;
    /* 0x8DBD */ bool field_0x8DBD;
    /* 0x8DBE */ u8 mMapChangeAlpha;
    /* 0x8DBF */ bool mIsVisible;
    /* 0x8DC0 */ UNKWORD field_0x8DC0;
    /* 0x8DC4 */ UNKWORD field_0x8DC4;
    /* 0x8DC8 */ dLytMapGlobal_c mGlobal;
};

// Size 0x91FC, inline ctor at 802ccd88
class dLytMap_c {
public:
    dLytMap_c() {
        mMapFader.setVisible(false);
        sInstance = this;
    }
    virtual ~dLytMap_c() {
        if (dScGame_c::GetInstance() != nullptr) {
            dScGame_c::GetInstance()->setTargetingScreenPrio(0x85);
        }
        sInstance = nullptr;
    }

    static d2d::ResAccIf_c *getResAcc() {
        return &sInstance->mResAcc;
    }

    static dLytMap_c *GetInstance() {
        return sInstance;
    }

    bool isVisible() const {
        return mMapMain.isVisible();
    }

    bool isOpen() const {
        return mMapMain.isOpen();
    }

    bool checkClose() const {
        return mMapMain.checkClose();
    }

    bool isMapIntroDone() const {
        return mMapMain.isMapIntroDone();
    }

    void close() {
        mMapMain.close();
    }

    static void initMapState() {
        dLytMapMain_c::initMapState();
    }

    bool isVisibleNoIntro() const {
        return mMapMain.isVisibleNoIntro();
    }

    void doFn_8013B350() {
        mMapMain.fn_8013B350();
    }

    f32 getOutDuration() const {
        return mMapMain.mOutDuration;
    }

    bool build();
    bool remove();
    bool execute();
    bool draw();

    bool isMapEventEq2Or4Or5Or6() const {
        return mMapMain.mMapEvent == dLytMapMain_c::MAP_EVENT_SW_BANK_SMALL ||
               mMapMain.mMapEvent == dLytMapMain_c::MAP_EVENT_DUNGEON_MAP_GET ||
               mMapMain.mMapEvent == dLytMapMain_c::MAP_EVENT_FIELD_MAP_CHANGE_5 ||
               mMapMain.mMapEvent == dLytMapMain_c::MAP_EVENT_FOREST_MAP_CHANGE;
    }

    bool isMapEventSaveObj() const {
        return mMapMain.mMapEvent == dLytMapMain_c::MAP_EVENT_SAVE_OBJ;
    }

    void startMapEvent(s32 mapEvent, s32 arg1, s32 arg2) {
        mMapMain.startMapEvent(mapEvent, arg1, arg2);
    }

    void setMapEventDone(bool done) {
        mMapMain.setMapEventDone(done);
    }

    void queueMapEvent(s32 mapEvent, s32 arg1, s32 arg2) {
        mMapMain.queueMapEvent(mapEvent, arg1, arg2);
    }

    const dMapSaveObjDefinition *getSaveObjDefinition(s32 statueIdx) const {
        return mMapMain.getSaveObjDefinition(mMapMain.mMapEventArg1, statueIdx);
    }

    void fadeOut();

private:
    /* 0x0004 */ d2d::ResAccIf_c mResAcc;
    /* 0x0374 */ dLytMapMain_c mMapMain;
    /* 0x91A4 */ mHeapAllocator_c field_0x91A4;
    /* 0x91C0 */ dLytMapFader_c mMapFader;

    static dLytMap_c *sInstance;
};

#endif
