#ifndef Z64_BGCHECK_H
#define Z64_BGCHECK_H

struct PlayState;
struct Actor;
struct DynaPolyActor;

#define SS_NULL 0xFFFF

#define COLPOLY_NORMAL_FRAC (1.0f / SHT_MAX)
#define COLPOLY_SNORMAL(x) ((s16)((x) * SHT_MAX))
#define COLPOLY_GET_NORMAL(n) ((n)*COLPOLY_NORMAL_FRAC)
#define COLPOLY_VIA_FLAG_TEST(vIA, flags) ((vIA) & (((flags)&7) << 13))
#define COLPOLY_VTX_INDEX(vI) ((vI)&0x1FFF)

#define DYNAPOLY_INVALIDATE_LOOKUP (1 << 0)

#define BGACTOR_NEG_ONE -1
#define BG_ACTOR_MAX 50
#define DYNA_WATERBOX_MAX 30
#define BGCHECK_SCENE BG_ACTOR_MAX
#define BGCHECK_Y_MAX 32000.0f
#define BGCHECK_Y_MIN -32000.0f
#define BGCHECK_XYZ_ABSMAX 32760.0f
#define BGCHECK_SUBDIV_OVERLAP 50
#define BGCHECK_SUBDIV_MIN 150.0f

#define WATERBOX_ROOM(p) ((((s32)p) >> 13) & 0x3F)

// bccFlags (bgcheck check flags)
#define BGCHECK_CHECK_WALL (1 << 0)
#define BGCHECK_CHECK_FLOOR (1 << 1)
#define BGCHECK_CHECK_CEILING (1 << 2)
#define BGCHECK_CHECK_ONE_FACE (1 << 3)
#define BGCHECK_CHECK_DYNA (1 << 4)
#define BGCHECK_CHECK_ALL \
    (BGCHECK_CHECK_WALL | BGCHECK_CHECK_FLOOR | BGCHECK_CHECK_CEILING | BGCHECK_CHECK_ONE_FACE | BGCHECK_CHECK_DYNA)

// bciFlags (bgcheck ignore flags)
#define BGCHECK_IGNORE_NONE 0
#define BGCHECK_IGNORE_CEILING (1 << 0)
#define BGCHECK_IGNORE_WALL (1 << 1)
#define BGCHECK_IGNORE_FLOOR (1 << 2)

// xpFlags (poly exclusion flags)
#define COLPOLY_IGNORE_NONE 0
#define COLPOLY_IGNORE_CAMERA (1 << 0)
#define COLPOLY_IGNORE_ENTITY (1 << 1)
#define COLPOLY_IGNORE_PROJECTILES (1 << 2)

// Surface Types
#define COLPOLY_SURFACE_GROUND 0
#define COLPOLY_SURFACE_SAND 1
#define COLPOLY_SURFACE_SNOW 14

// CollisionContext flags
#define BGCHECK_FLAG_REVERSE_CONVEYOR_FLOW 1

typedef struct {
    /* 0x0 */ Vec3s pos;
} BgVertex; // size = 0x6

typedef struct {
    /* 0x00 */ Vec3f scale;
    /* 0x0C */ Vec3s rot;
    /* 0x14 */ Vec3f pos;
} ScaleRotPos; // size = 0x20

typedef struct CollisionPoly {
    /* 0x0 */ u16 type;
    union {
        /* 0x2 */ u16 vtxData[3];
        struct {
            /* 0x2 */ u16 flags_vIA; // 0xE000 is poly exclusion flags (xpFlags), 0x1FFF is vtxId
            /* 0x4 */ u16 flags_vIB; // 0xE000 is flags, 0x1FFF is vtxId
                                      // 0x2000 = poly IsConveyor surface
            /* 0x6 */ u16 vIC;
        };
    };
    /* 0x8 */ Vec3s normal; // Unit normal vector
                             // Value ranges from -0x7FFF to 0x7FFF, representing -1.0 to 1.0; 0x8000 is invalid

    /* 0xE */ s16 dist; // Plane distance from origin along the normal
} CollisionPoly; // size = 0x10

typedef struct {
    /* 0x0 */ u16 setting; // camera setting described by CameraSettingType enum
    /* 0x2 */ s16 count; // only used when `bgCamFuncData` is a list of points used for crawlspaces (unused oot legacy)
    /* 0x4 */ Vec3s* bgCamFuncData; // s16 data grouped in threes (ex. Vec3s), is usually of type `BgCamFuncData`
} BgCamInfo; // size = 0x8

typedef BgCamInfo CamData; // TODO: ZAPD compatibility

// The structure used for all instances of s16 data from `BgCamInfo`.
typedef struct {
    /* 0x00 */ Vec3s pos;
    /* 0x06 */ Vec3s rot;
    /* 0x0C */ s16 fov;
    /* 0x0E */ s16 unk_0E;
    /* 0x10 */ s16 unk_10;
} BgCamFuncData; // size = 0x12

typedef struct {
    /* 0x0 */ Vec3s minPos;
    /* 0x6 */ s16 xLength;
    /* 0x8 */ s16 zLength;
    /* 0xC */ u32 properties;
    // 0x0008_0000 = ?
    // 0x0007_E000 = room index, 0x3F = all rooms
    // 0x0000_1F00 = lighting setting index
    // 0x0000_00FF = CamData index
} WaterBox; // size = 0x10

typedef struct {
    /* 0x0 */ u32 data[2];

    // Word 0
    // 0x8000_0000 = horse blocked
    // 0x4000_0000 = floor surface recessed by 1 unit
    // 0x3C00_0000 = ? (floor property?)
    // 0x03E0_0000 = ? (wall property?)
    // 0x001C_0000 = ?
    // 0x0003_E000 = ?
    // 0x0000_1F00 = scene exit index
    // 0x0000_00FF = CamData index
    // Word 1
    // 0x0800_0000 = wall damage
    // 0x07E0_0000 = conveyor direction
    // 0x001C_0000 = conveyor speed
    // 0x0002_0000 = is hookable surface
    // 0x0001_F800 = echo/reverb
    // 0x0000_07C0 = lighting setting index
    // 0x0000_0030 = surface slope
    // 0x0000_000F = type

} SurfaceType; // size = 0x8

typedef struct {
    /* 0x00 */ Vec3s minBounds; // minimum coordinates of poly bounding box
    /* 0x06 */ Vec3s maxBounds; // maximum coordinates of poly bounding box
    /* 0x0C */ u16 numVertices;
    /* 0x10 */ Vec3s* vtxList;
    /* 0x14 */ u16 numPolygons;
    /* 0x18 */ CollisionPoly* polyList;
    /* 0x1C */ SurfaceType* surfaceTypeList;
    /* 0x20 */ CamData* bgCamList;
    /* 0x24 */ u16 numWaterBoxes;
    /* 0x28 */ WaterBox* waterBoxes;
} CollisionHeader; // size = 0x2C

typedef struct {
    /* 0x0 */ s16 polyId;
    /* 0x2 */ u16 next; // index of the next SSNode in the list, or SS_NULL if last element 
} SSNode; // size = 0x4

// represents a linked list of type SSNode
typedef struct {
    /* 0x0 */ u16 head; // index of the first SSNode in the list, or SS_NULL if the list is empty
} SSList; // size = 0x2

typedef struct {
    /* 0x0 */ u16 max;
    /* 0x2 */ u16 count;
    /* 0x4 */ SSNode* tbl;
    /* 0x8 */ u8* polyCheckTbl;
} SSNodeList; // size = 0xC

typedef struct {
    /* 0x0 */ SSNode* tbl;
    /* 0x4 */ u32 count;
    /* 0x8 */ s32 maxNodes;
} DynaSSNodeList; // size = 0xC

typedef struct {
    /* 0x0 */ s32 unk0;
    /* 0x4 */ WaterBox* boxes;
} DynaWaterBoxList; // size = 0x8

typedef struct {
    /* 0x0 */ SSList floor;
    /* 0x2 */ SSList wall;
    /* 0x4 */ SSList ceiling;
} StaticLookup; // size = 0x6

typedef struct {
    /* 0x0 */ s16 polyStartIndex;
    /* 0x2 */ SSList ceiling;
    /* 0x4 */ SSList wall;
    /* 0x6 */ SSList floor;
} DynaLookup; // size = 0x8

typedef struct {
    /* 0x00 */ Actor* actor;
    /* 0x04 */ CollisionHeader* colHeader;
    /* 0x08 */ DynaLookup dynaLookup;
    /* 0x10 */ u16 vtxStartIndex;
    /* 0x12 */ u16 waterboxesStartIndex;
    /* 0x14 */ ScaleRotPos prevTransform;
    /* 0x34 */ ScaleRotPos curTransform;
    /* 0x54 */ Sphere16 boundingSphere;
    /* 0x5C */ f32 minY;
    /* 0x60 */ f32 maxY;
} BgActor; // size = 0x64

typedef struct {
    /* 0x0000 */ u8 bitFlag;
    /* 0x0004 */ BgActor bgActors[BG_ACTOR_MAX];
    /* 0x138C */ u16 bgActorFlags[BG_ACTOR_MAX]; // bit 0 - Is mesh active
    /* 0x13F0 */ CollisionPoly* polyList;
    /* 0x13F4 */ Vec3s* vtxList;
    /* 0x13F8 */ DynaWaterBoxList waterBoxList;
    /* 0x1400 */ DynaSSNodeList polyNodes;
    /* 0x140C */ s32 polyNodesMax;
    /* 0x1410 */ s32 polyListMax;
    /* 0x1414 */ s32 vtxListMax;
} DynaCollisionContext; // size = 0x1418

typedef struct {
    /* 0x00 */ CollisionHeader* colHeader; // scene's static collision
    /* 0x04 */ Vec3f minBounds;            // minimum coordinates of collision bounding box
    /* 0x10 */ Vec3f maxBounds;            // maximum coordinates of collision bounding box
    /* 0x1C */ Vec3i subdivAmount;         // x, y, z subdivisions of the scene's static collision
    /* 0x28 */ Vec3f subdivLength;         // x, y, z subdivision worldspace lengths
    /* 0x34 */ Vec3f subdivLengthInv;      // inverse of subdivision length
    /* 0x40 */ StaticLookup* lookupTbl;    // 3d array of length subdivAmount
    /* 0x44 */ SSNodeList polyNodes;
    /* 0x0050 */ DynaCollisionContext dyna;
    /* 0x1468 */ u32 memSize; // Size of all allocated memory plus CollisionContext
    /* 0x146C */ u32 flags;   // bit 0 reverses conveyor direction (i.e. water flow in Great Bay Temple)
} CollisionContext; // size = 0x1470

typedef struct {
    /* 0x00 */ struct PlayState* play;
    /* 0x04 */ CollisionContext* colCtx;
    /* 0x08 */ u16 xpFlags;
    /* 0x0C */ CollisionPoly** resultPoly;
    /* 0x10 */ f32 yIntersect;
    /* 0x14 */ Vec3f* pos;
    /* 0x18 */ s32* bgId;
    /* 0x1C */ s32 unk1C;
    /* 0x20 */ struct Actor* actor;
    /* 0x24 */ u32 unk_24;
    /* 0x28 */ f32 checkDist;
    /* 0x2C */ DynaCollisionContext* dyna;
    /* 0x30 */ SSList* ssList;
} DynaRaycast; // size = 0x34

typedef struct
{
    /* 0x00 */ StaticLookup* lookup;
    /* 0x04 */ SSList* ssList;
    /* 0x08 */ CollisionContext* colCtx;
    /* 0x0C */ u16 xpFlags1;
    /* 0x0E */ u16 xpFlags2;
    /* 0x10 */ Vec3f* posA;
    /* 0x14 */ Vec3f* posB;
    /* 0x18 */ Vec3f* outPos;
    /* 0x1C */ CollisionPoly** outPoly;
    /* 0x20 */ f32 outDistSq;
    /* 0x24 */ f32 checkDist;
    /* 0x28 */ s32 bccFlags;
    /* 0x2C */ Actor* actor;
} StaticLineTest; // size = 0x30

typedef struct {
    /* 0x00 */ CollisionContext* colCtx;
    /* 0x04 */ u16 xpFlags;
    /* 0x08 */ DynaCollisionContext* dyna;
    /* 0x0C */ SSList* ssList;
    /* 0x10 */ Vec3f* posA;
    /* 0x14 */ Vec3f* posB;
    /* 0x18 */ Vec3f* posResult;
    /* 0x1C */ CollisionPoly** resultPoly;
    /* 0x20 */ s32 checkOneFace; // bccFlags & 0x8
    /* 0x24 */ f32* distSq;      // distance from posA to poly squared
    /* 0x28 */ f32 checkDist;    // distance from poly
    /* 0x2C */ Actor* actor;
    /* 0x30 */ s32 bgId;
} DynaLineTest; // size = 0x34

typedef struct {
    /* 0x00 */ CollisionPoly* poly;
    /* 0x04 */ Vec3s* vtxList;
    /* 0x08 */ Vec3f* posA;
    /* 0x0C */ Vec3f* posB;
    /* 0x10 */ Vec3f* planeIntersect;
    /* 0x14 */ s32 checkOneFace;
    /* 0x18 */ f32 checkDist;
} BgLineVsPolyTest; // size = 0x1C

typedef struct {
    /* 0x0 */ s16 sceneId;
    /* 0x4 */ u32 memSize;
} BgSpecialSceneMaxMemory; // size = 0x8

typedef struct {
    /* 0x0 */ s16 sceneId;
    /* 0x2 */ s16 maxNodes;
    /* 0x4 */ s16 maxPolygons;
    /* 0x6 */ s16 maxVertices;
} BgSpecialSceneMaxObjects; // size = 0x8

typedef struct {
    /* 0x0 */ s16 sceneId;
    /* 0x2 */ s16 xSubdivisions;
    /* 0x4 */ s16 ySubdivisions;
    /* 0x6 */ s16 zSubdivisions;
    /* 0x8 */ s32 unk8;
} BgSpecialSceneMeshSubdivision; // size = 0xC

typedef struct {
    /* 0x0 */ s16 sceneId;
    /* 0x2 */ Vec3s subdivAmount;
    /* 0x8 */ s32 nodeListMax; // if -1, dynamically compute max nodes
} BgCheckSceneSubdivisionEntry; // size = 0xC

#endif
