extern cKF_Animation_R_c cKF_ba_r_obj_s_shop1;
extern cKF_Animation_R_c cKF_ba_r_obj_w_shop1;

// clang-format off
static Door_data_c aSHOP_shop_door_data = {
    SCENE_SHOP0,
    mSc_DIRECT_NORTH,
    FALSE,
    0,
    {160,0,300},
    EMPTY_NO,
    1,
    {0,0,0},
};
// clang-format on

static void aSHOP_set_bgOffset(STRUCTURE_ACTOR* shop, int offs) {
    // clang-format off
    static mCoBG_OffsetTable_c height_table_ct[] = {
        { mCoBG_ATTRIBUTE_NONE, 0, 0, 0, 0, 0, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 0, 12, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 0, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 0, 0, 0, 0, 0, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 0, 12, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 12, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 12, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 0, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 12, 0, 12, 12, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 12, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 12, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 0, 1 },
        { mCoBG_ATTRIBUTE_NONE, 0, 0, 0, 0, 0, 0 },
        { mCoBG_ATTRIBUTE_NONE, 12, 0, 12, 12, 12, 1 },
        { mCoBG_ATTRIBUTE_NONE, 12, 12, 12, 12, 0, 1 },
        { mCoBG_ATTRIBUTE_NONE, 0, 0, 0, 0, 0, 0 }
    };
    // clang-format on

    static mCoBG_OffsetTable_c* height_table[] = { height_table_ct, height_table_ct };

    static f32 addX[] = { -(mFI_UT_WORLDSIZE_X_F * 2.0f), -mFI_UT_WORLDSIZE_X_F, 0.0f, mFI_UT_WORLDSIZE_X_F };
    static f32 addZ[] = { (mFI_UT_WORLDSIZE_Z_F * 2.0f), mFI_UT_WORLDSIZE_Z_F, 0.0f, -mFI_UT_WORLDSIZE_Z_F };

    mCoBG_OffsetTable_c* offset;
    int i;
    int j;
    xyz_t pos;

    offset = height_table[offs];
    for (i = 0; i < 4; i++) {
        pos.z = shop->actor_class.home.position.z + addZ[i];

        for (j = 0; j < 4; j++) {
            if (j + i * 4 != 0 && j + i * 4 != 3 && j + i * 4 != 12 && j + i * 4 != 15) {
                pos.x = shop->actor_class.home.position.x + addX[j];
                mCoBG_SetPluss5PointOffset_file(pos, *offset, __FILE__, 221);
            }

            offset++;
        }
    }
}

static void aSHOP_rewrite_out_data(ACTOR* actor, GAME_PLAY* play) {
    Door_data_c* door_data;
    xyz_t pos;

    door_data = Common_GetPointer(structure_exit_door_data);
    if (play->fb_wipe_mode != 0) {
        return;
    }

    door_data->next_scene_id = Save_Get(scene_no);
    door_data->exit_orientation = mSc_DIRECT_SOUTH_WEST;
    door_data->exit_type = 0;
    door_data->extra_data = 3;

    pos.x = actor->world.position.x - 68.29f;
    pos.z = actor->world.position.z + 68.29f;
    pos.y = mCoBG_GetBgY_OnlyCenter_FromWpos2(pos, 0.0f);

    door_data->exit_position.x = pos.x;
    door_data->exit_position.y = pos.y;
    door_data->exit_position.z = pos.z;

    door_data->door_actor_name = SHOP0;

    door_data->wipe_type = 1;

    mBGMPsComp_make_ps_wipe(0x2168);
}

static int aSHOP_check_player2(GAME_PLAY* play) {
    PLAYER_ACTOR* player;
    u16 y;
    int res;

    player = GET_PLAYER_ACTOR(play);
    y = player->actor_class.world.angle.y;

    res = 0;
    if ((y > DEG2SHORT_ANGLE2(90.0f)) && (y < DEG2SHORT_ANGLE2(180.0f)) &&
        (player->actor_class.speed > 0.0f)) { // 0x6000 && 0xA000
        res = 1;
    }

    return res;
}

static int aSHOP_check_player(ACTOR* actor, GAME_PLAY* play) {
    u16 y;
    f32 xOffs;
    f32 zOffs;
    f32 t;
    int res;
    PLAYER_ACTOR* player;

    player = GET_PLAYER_ACTOR(play);
    res = 0;

    if (player == NULL) {
        return 0;
    }

    y = player->actor_class.shape_info.rotation.y;
    xOffs = SQ(player->actor_class.world.position.x - (actor->world.position.x - 38.0f));
    zOffs = SQ(player->actor_class.world.position.z - (actor->world.position.z + 42.0f));
    t = (xOffs) + (zOffs);

    if ((y > DEG2SHORT_ANGLE2(90.0f)) && (y < DEG2SHORT_ANGLE2(180.0f)) && (t < 550.0f)) {
        res = 1;
    }

    return res;
}

static void aSHOP_set_talk_info_close_wait(ACTOR* actor) {
    rgba_t window_color;
    int msg_no;
    int now_sec;
    int status;

    now_sec = Common_Get(time.now_sec);
    if (mSP_CheckFukubikiDay() != FALSE) {
        if (now_sec >= (6 * mTM_SECONDS_IN_HOUR)) {
            msg_no = mSP_ShopOpen() == mSP_SHOP_STATUS_PRE ? 0x7D6 : 0x7D7;
        } else {
            msg_no = 0x7CD;
        }
    } else {
        switch (mSP_ShopOpen()) {
            case mSP_SHOP_STATUS_PRE: // 0
            case mSP_SHOP_STATUS_END: // 1
                msg_no = 0x7CD;
                break;
            case mSP_SHOP_STATUS_PREEVENT: // 4
                msg_no = 0x7D1;
                break;
            case mSP_SHOP_STATUS_ENDEVENT: // 5
                msg_no = 0x7D9;
                break;
            case mSP_SHOP_STATUS_RENEW: // 3
                msg_no = 0x7D8;
                break;
            default:
                msg_no = 0x7CD;
                break;
        }
    }

    mDemo_Set_msg_num(msg_no);

    mDemo_Set_talk_display_name(FALSE);
    mDemo_Set_camera(TRUE);
    mPlib_Set_able_hand_all_item_in_demo(TRUE);
    mDemo_Set_ListenAble();

    window_color.r = 145;
    window_color.g = 60;
    window_color.b = 40;
    window_color.a = 255;
    mDemo_Set_talk_window_color(&window_color);
}

static int aSHOP_ctrl_light(STRUCTURE_ACTOR* shop) {
    int now_sec;
    int res;

    now_sec = Common_Get(time.now_sec);
    res = FALSE;
    if (mEv_CheckArbeit() == TRUE) {
        if ((mSP_ShopOpen() == mSP_SHOP_STATUS_OPEN || mSP_ShopOpen() == mSP_SHOP_STATUS_OPENEVENT)) {
            if ((now_sec >= (18 * mTM_SECONDS_IN_HOUR) || now_sec < (5 * mTM_SECONDS_IN_HOUR))) {
                res = TRUE;
            }
        }
    } else if (now_sec >= (18 * mTM_SECONDS_IN_HOUR) || now_sec < (5 * mTM_SECONDS_IN_HOUR)) {
        if (shop->action == aSHOP_ACTION_PL_INTO_WAIT) {
            res = TRUE;
        } else if (mSP_ShopOpen() == mSP_SHOP_STATUS_OPEN || mSP_ShopOpen() == mSP_SHOP_STATUS_OPENEVENT) {
            res = TRUE;
        }
    }

    return res;
}

static void aSHOP_close_wait(STRUCTURE_ACTOR* shop, GAME_PLAY* play) {
    ACTOR* actor;

    actor = (ACTOR*)shop;

    if (mDemo_Check(mDemo_TYPE_TALK, actor) == TRUE) {
        return;
    }

    if ((mSP_ShopOpen() == mSP_SHOP_STATUS_OPEN) || (mSP_ShopOpen() == mSP_SHOP_STATUS_OPENEVENT)) {
        aSHOP_setup_action(shop, aSHOP_ACTION_OPEN_DOOR);
        return;
    }

    if (aSHOP_check_player(actor, play) == mSP_SHOP_STATUS_PRE) {
        return;
    }

    mDemo_Request(mDemo_TYPE_TALK, actor, &aSHOP_set_talk_info_close_wait);
}

static void aSHOP_open_wait(STRUCTURE_ACTOR* shop, GAME_PLAY* play) {
    ACTOR* actor;
    GAME* game;
    xyz_t pos;

    actor = &shop->actor_class;
    game = &play->game;

    if (mPlib_check_player_actor_main_index_OutDoorMove(&play->game) != FALSE) {
        return;
    }

    if (mSP_ShopOpen() != mSP_SHOP_STATUS_OPEN && mSP_ShopOpen() != mSP_SHOP_STATUS_OPENEVENT) {
        sAdo_OngenTrgStart(8, &actor->world.position);
        aSHOP_setup_action(shop, aSHOP_ACTION_CLOSE_DOOR);
        return;
    }

    if (aSHOP_check_player(actor, play) == 1 && aSHOP_check_player2(play) == TRUE) {
        pos.x = actor->world.position.x - 50.0f;
        pos.y = GET_PLAYER_ACTOR(play)->actor_class.world.position.y;
        pos.z = actor->world.position.z + 50.0f;
        mSP_SetTanukiShopStatus();

        if (mPlib_request_main_door_type1(game, &pos, DEG2SHORT_ANGLE(135.0f), TRUE, actor) == 0) { // 0x6000
            return;
        }

        aSHOP_setup_action(shop, aSHOP_ACTION_PL_INTO_WAIT);
    }
}

static void aSHOP_close_door(STRUCTURE_ACTOR* shop, GAME_PLAY* play) {
    if (shop->keyframe_state != 1) {
        return;
    }

    sAdo_OngenTrgStart(9, &shop->actor_class.world.position);
    aSHOP_setup_action(shop, aSHOP_ACTION_CLOSE_WAIT);
}

static void aSHOP_open_door(STRUCTURE_ACTOR* shop, GAME_PLAY* play) {
    if (shop->keyframe_state != 1) {
        return;
    }

    aSHOP_setup_action(shop, aSHOP_ACTION_OPEN_WAIT);
}

static void aSHOP_pl_into_wait(STRUCTURE_ACTOR* shop, GAME_PLAY* play) {
    if (shop != GET_PLAYER_ACTOR_NOW()->get_door_label_proc(gamePT)) {
        return;
    }

    aSHOP_rewrite_out_data(&shop->actor_class, play);
    goto_other_scene(play, &aSHOP_shop_door_data, FALSE);
}

static void aSHOP_setup_action(STRUCTURE_ACTOR* shop, int action) {
    static cKF_Animation_R_c* ani[] = { &cKF_ba_r_obj_s_shop1, &cKF_ba_r_obj_w_shop1 };
    static f32 anime_spd[] = { 0.0f, 0.0f, 0.5f, 0.5f, 0.0f };
    static f32 set[] = { 1.0f, 16.0f, 16.0f, 1.0f, 16.0f };
    static f32 end[] = { 1.0f, 16.0f, 1.0f, 16.0f, 16.0f };

    static aSTR_MOVE_PROC process[] = { &aSHOP_close_wait, &aSHOP_open_wait, &aSHOP_close_door, &aSHOP_open_door,
                                        &aSHOP_pl_into_wait };

    int winter;

    winter = shop->season == mTM_SEASON_WINTER;

    cKF_SkeletonInfo_R_init(&shop->keyframe, shop->keyframe.skeleton, ani[winter], 1.0f, end[action], set[action],
                            anime_spd[action], 0.0f, cKF_FRAMECONTROL_STOP, NULL);

    shop->action_proc = process[action];
    shop->action = action;
}

static void aSHOP_actor_move(ACTOR* actor, GAME* game) {
    STRUCTURE_ACTOR* shop;
    GAME_PLAY* play;
    s16 target;
    xyz_t pos;

    shop = (STRUCTURE_ACTOR*)actor;
    play = (GAME_PLAY*)game;

    shop->keyframe_state = cKF_SkeletonInfo_R_play(&shop->keyframe);
    shop->keyframe_saved_keyframe = shop->keyframe.frame_control.current_frame;

    (*shop->action_proc)(shop, play);

    target = (s16)shop->arg0;
    if (aSHOP_ctrl_light(shop)) {
        chase_s(&target, DEG2SHORT_ANGLE(90.0f) - 1, 320); // 0x3FFF
    } else {
        chase_s(&target, DEG2SHORT_ANGLE(0.0f), 320);
    }

    shop->arg0 = target;
}

static void aSHOP_actor_init(ACTOR* actor, GAME* game) {
    mFI_SetFG_common(DUMMY_SHOP0, actor->home.position, FALSE);
    aSHOP_actor_move(actor, game);
    actor->mv_proc = aSHOP_actor_move;
}
