#ifndef AC_FURNITURE_H
#define AC_FURNITURE_H

#include "types.h"
#include "m_collision_bg.h"
#include "c_keyframe.h"
#include "m_actor.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct furniture_actor_s FTR_ACTOR;

#define aFTR_KEEP_ITEM_COUNT (mCoBG_LAYER_NUM - 1)
#define aFTR_CHECK_INTERACTION(inter, type) (((inter) >> (type)) & 1)

#define aFTR_EDGE_COL_NUM 4

enum {
    aFTR_STATE_STOP,
    aFTR_STATE_WAIT_PUSH,
    aFTR_STATE_WAIT_PUSH2,
    aFTR_STATE_WAIT_PUSH3,
    aFTR_STATE_PUSH,
    aFTR_STATE_WAIT_PULL,
    aFTR_STATE_WAIT_PULL2,
    aFTR_STATE_PULL,
    aFTR_STATE_WAIT_LROTATE,
    aFTR_STATE_LROTATE,
    aFTR_STATE_WAIT_RROTATE,
    aFTR_STATE_RROTATE,
    aFTR_STATE_BIRTH_WAIT,
    aFTR_STATE_BIRTH,
    aFTR_STATE_BYE,
    aFTR_STATE_DEATH,

    aFTR_STATE_NUM
};

enum {
    aFTR_CTR_TYPE_GAME,
    aFTR_CTR_TYPE_GAME_PLAY,

    aFTR_CTR_TYPE_NUM
};

enum {
    aFTR_HNW_STATE_WAIT_TURN_ON,
    aFTR_HNW_STATE_TURN_ON,
    aFTR_HNW_STATE_ON,
    aFTR_HNW_STATE_OFF,

    aFTR_HNW_STATE_NUM
};

enum {
    aFTR_SHAPE_TYPEB_90,  /* 2x1 (facing up)*/
    aFTR_SHAPE_TYPEB_180, /* 2x1 [- *] */
    aFTR_SHAPE_TYPEB_270, /* 2x1 (facing down) */
    aFTR_SHAPE_TYPEB_0,   /* 2x1 [* -] */
    aFTR_SHAPE_TYPEA,     /* 1x1 */
    aFTR_SHAPE_TYPEC,     /* 2x2 */

    aFTR_SHAPE_TYPE_NUM
};

enum {
    aFTR_INTERACTION_NONE = 0,
    aFTR_INTERACTION_STORAGE_DRAWERS = 1,  // dressers
    aFTR_INTERACTION_STORAGE_WARDROBE = 2, // double doors
    aFTR_INTERACTION_STORAGE_CLOSET = 4,   // single door
    aFTR_INTERACTION_MUSIC_DISK = 8,
    aFTR_INTERACTION_NO_COLLISION = 0x10,
    aFTR_INTERACTION_HANIWA = 0x20,
    aFTR_INTERACTION_FISH = 0x40,
    aFTR_INTERACTION_INSECT = 0x80,
    aFTR_INTERACTION_MANNEKIN = 0x100,
    aFTR_INTERACTION_UMBRELLA = 0x200,
    aFTR_INTERACTION_FOSSIL = 0x400,
    aFTR_INTERACTION_FAMICOM = 0x800,
    aFTR_INTERACTION_START_DISABLED = 0x1000, // mainly used for TVs to be off when placed
    // there may be more types, but I didn't see any
    aFTR_INTERACTION_FAMICOM_ITEM = 0x2000,
    aFTR_INTERACTION_RADIO_AEROBICS = 0x4000,
    aFTR_INTERACTION_TOGGLE = 0x8000,
};

enum {
    aFTR_INTERACTION_TYPE_DRAWERS,
    aFTR_INTERACTION_TYPE_WARDROBE,
    aFTR_INTERACTION_TYPE_CLOSET,
    aFTR_INTERACTION_TYPE_MUSIC_DISK,
    aFTR_INTERACTION_TYPE_NO_COLLISION,
    aFTR_INTERACTION_TYPE_HANIWA,
    aFTR_INTERACTION_TYPE_FISH,
    aFTR_INTERACTION_TYPE_INSECT,
    aFTR_INTERACTION_TYPE_MANNEKIN,
    aFTR_INTERACTION_TYPE_UMBRELLA,
    aFTR_INTERACTION_TYPE_FOSSIL,
    aFTR_INTERACTION_TYPE_FAMICOM,
    aFTR_INTERACTION_TYPE_START_DISABLED,
    aFTR_INTERACTION_TYPE_FAMICOM_ITEM,
    aFTR_INTERACTION_TYPE_RADIO_AEROBICS,
    aFTR_INTERACTION_TYPE_TOGGLE,

    aFTR_INTERACTION_NUM,
};

#define aFTR_IS_STORAGE(profile)                                                            \
    (aFTR_CHECK_INTERACTION((profile)->interaction_type, aFTR_INTERACTION_TYPE_DRAWERS) ||  \
     aFTR_CHECK_INTERACTION((profile)->interaction_type, aFTR_INTERACTION_TYPE_WARDROBE) || \
     aFTR_CHECK_INTERACTION((profile)->interaction_type, aFTR_INTERACTION_TYPE_CLOSET) ||   \
     aFTR_CHECK_INTERACTION((profile)->interaction_type, aFTR_INTERACTION_TYPE_MUSIC_DISK))
enum {
    aFTR_CONTACT_ACTION_NONE = 0,
    aFTR_CONTACT_ACTION_CHAIR_UNIDIRECTIONAL = 1,   // only can sit from the front
    aFTR_CONTACT_ACTION_CHAIR_MULTIDIRECTIONAL = 2, // can sit from any direction
    aFTR_CONTACT_ACTION_CHAIR_SOFA = 4,             // cam sit anywhere from the front
    aFTR_CONTACT_ACTION_BED_SINGLE = 8,             // single bed (can't roll)
    aFTR_CONTACT_ACTION_BED_DOUBLE = 0x10,          // double bed (can roll)

    aFTR_CONTACT_ACTION_NUM
};

enum {
    aFTR_CONTACT_ACTION_TYPE_CHAIR1,
    aFTR_CONTACT_ACTION_TYPE_CHAIR4,
    aFTR_CONTACT_ACTION_TYPE_SOFA,
    aFTR_CONTACT_ACTION_TYPE_BED_SINGLE,
    aFTR_CONTACT_ACTION_TYPE_BED_DOUBLE,

    aFTR_CONTACT_ACTION_TYPE_NUM
};

#define aFTR_CHK_CONTACT_ACTION(cnt, action) (((cnt) >> (aFTR_CONTACT_ACTION_TYPE_##action)) & 1)
#define aFTR_CHK_CHAIR(cnt) \
    (aFTR_CHK_CONTACT_ACTION(cnt, CHAIR1) || aFTR_CHK_CONTACT_ACTION(cnt, CHAIR4) || aFTR_CHK_CONTACT_ACTION(cnt, SOFA))
#define aFTR_CHK_BED(cnt) (aFTR_CHK_CONTACT_ACTION(cnt, BED_SINGLE) || aFTR_CHK_CONTACT_ACTION(cnt, BED_DOUBLE))

enum {
    aFTR_SET_TYPE_NORMAL,     /* Can't be placed on top and is not a table (layer0) */
    aFTR_SET_TYPE_SURFACE,    /* Is a surface (layer0) */
    aFTR_SET_TYPE_ON_SURFACE, /* Can be placed on a surface (layer0/layer1) */

    aFTR_SET_TYPE_NUM
};

enum {
    aFTR_KANKYO_MAP_NONE,
    aFTR_KANKYO_MAP_OPA,
    aFTR_KANKYO_MAP_XLU,

    aFTR_KANKYO_MAP_NUM
};

typedef void (*aFTR_FTR_CT_PROC)(FTR_ACTOR*, u8*);
typedef void (*aFTR_FTR_MOVE_PROC)(FTR_ACTOR*, ACTOR*, GAME*, u8*);
typedef void (*aFTR_FTR_DRAW_PROC)(FTR_ACTOR*, ACTOR*, GAME*, u8*);
typedef void (*aFTR_FTR_DT_PROC)(FTR_ACTOR*, u8*);
typedef void (*aFTR_FTR_DMA_PROC)(mActor_name_t, u8*);

typedef struct ftr_vtbl_s {
    aFTR_FTR_CT_PROC ct_proc;
    aFTR_FTR_MOVE_PROC move_proc;
    aFTR_FTR_DRAW_PROC draw_proc;
    aFTR_FTR_DT_PROC dt_proc;
    aFTR_FTR_DMA_PROC dma_proc;
} aFTR_vtable_c;

typedef struct ftr_rig_s {
    cKF_Skeleton_R_c* skeleton;
    cKF_Animation_R_c* animation;
    f32 speed;
} aFTR_rig_c;

typedef struct ftr_tex_animation_s {
    u8** animation;
    int animation_count;
} aFTR_tex_anim_c;

typedef struct ftr_profile_s {
    Gfx* opaque0;
    Gfx* opaque1;
    Gfx* translucent0;
    Gfx* translucent1;
    u8* texture;
    u16* palette;
    aFTR_rig_c* rig;
    aFTR_tex_anim_c* tex_anim;
    f32 height;
    f32 scale;
    u8 shape;              /* aFTR_SHAPE_TYPE* */
    u8 move_bg_type;       /* mCoBG_FTR_TYPE* */
    u8 check_rotation;     /* when non-zero extra checks are performed for items in the way during rotation? */
    u8 kankyo_map;         /* lighting map */
    u8 contact_action;     /* action on contact */
    u16 interaction_type;  /* action when interacting */
    aFTR_vtable_c* vtable; /* unique furniture functions */
} aFTR_PROFILE;

typedef struct furniture_collision_s {
    f32 start[2];           /* starting xz pos */
    f32 end[2];             /* ending xz pos */
    f32 normal[2];          /* xz normal */
    f32 height;             /* collision y */
    f32 edge_contact_ratio; /* percentage of the xz collision in contact with edge to count as collision */
} aFTR_collision_c;

struct furniture_actor_s {
    u16 name;
    s16 ctr_type; // 0 = GAME's frame counter, 1 = GAME_PLAY's frame counter
    int id;
    xyz_t position;
    xyz_t last_position;
    xyz_t target_position;
    int target_direction; /* direction to target position */
    f32 player_distance;  /* distance to the player */
    f32 angle_y;          /* current Y angle */
    f32 angle_y_target;   /* goal Y angle */
    s16 state;
    u8 shape_type;          /* current size & shape (rotation) */
    u8 original_shape_type; /* original size & shape (rotation) */
    xyz_t base_position;
    aFTR_collision_c edge_collision[aFTR_EDGE_COL_NUM]; /* collision for each edge */
    s16 collision_direction;
    int move_bg_idx;
    mCoBG_bg_regist_c bg_register;
    mCoBG_bg_contact_c bg_contact;
    s16 s_angle_y; /* current Y angle but 'binangle' format */
    f32 collision_scale;
    u8 switch_bit;          /* furniture on/off state */
    u8 switch_changed_flag; /* signal that the on/off state is toggled */
    s8 haniwa_step;
    s16 haniwa_state;
    cKF_SkeletonInfo_R_c keyframe; /* for actor animations */
    s_xyz joint[9];
    s_xyz morph[9];
    Mtx skeleton_mtx[2][12];
    cKF_TextureAnimation_c tex_animation; /* used for texture animations like TVs */
    xyz_t scale;
    f32 birth_scale_modifier; /* modifies scale for "birth" animation */
    s16 birth_anim_counter;
    s16 birth_anim_step;
    s16 rotation_delay_timer;
    s16 dynamic_work_s[5]; /* reserved for any use by each unique furniture actor */
    f32 dynamic_work_f[2]; /* reserved for any use by each unique furniture actor */
    s16 layer;             /* layer the furniture actor resides on */
    s16 _83E;
    s16 demo_status; /* set when a music player is interacted with */
    s16 dust_timer;
    mActor_name_t items[aFTR_KEEP_ITEM_COUNT]; /* used for holding items (music players & wardrobes)  */
    int _84C;
    u16* pal_p; /* used for furniture actors with dynamic palettes such as the structure model items */
    int _854;
};

#ifdef __cplusplus
}
#endif

#endif
