#include "m_post_office.h"

#include "m_mail.h"
#include "m_npc.h"
#include "m_handbill.h"
#include "m_museum.h"
#include "m_name_table.h"
#include "m_item_name.h"
#include "m_private.h"
#include "m_string.h"
#include "m_house.h"
#include "m_event.h"
#include "m_field_info.h"
#include "m_play.h"
#include "libultra/libultra.h"
#include "m_common_data.h"
#include "m_scene_table.h"

static int mPO_keep_contents(Mail_c* mail) {
    int res = FALSE;

    int free_idx = mMl_chk_mail_free_space(Save_Get(post_office).mail, mPO_MAIL_STORAGE_SIZE);
    if (free_idx >= 0) {
        mMl_copy_mail(Save_Get(post_office).mail + free_idx, mail);
        mMl_clear_mail(mail);
        res = TRUE;
    }

    return res;
}

extern int mPO_count_mail(int house_no) {
    int used = mMl_count_use_mail_space(Save_Get(homes[house_no]).mailbox, HOME_MAILBOX_SIZE);
    Mail_c* mail = Save_Get(post_office).mail;
    int i;

    for (i = 0; i < mPO_MAIL_STORAGE_SIZE; i++) {
        if (house_no == mMl_hunt_for_send_address(mail)) {
            used++;
        }

        mail++;
    }

    return used;
}

extern int mPO_get_keep_mail_sum() {
    PostOffice_c* post_office = Save_GetPointer(post_office);

    return post_office->keep_mail_sum_players + post_office->keep_mail_sum_npcs;
}

static void mPO_adjust_keep_mail() {
    Mail_c* mail = Save_Get(post_office).mail;
    int i;
    s16 recipient_flags = 0;
    s16 keep_mail_player_num = 0;

    for (i = 0; i < mPO_MAIL_STORAGE_SIZE; i++) {
        if (mMl_check_not_used_mail(mail) == FALSE) {
            int house_idx = mMl_hunt_for_send_address(mail);
            if (house_idx != -1) {
                keep_mail_player_num++;
                recipient_flags |= (1 << house_idx);
            } else {
                mMl_clear_mail(mail);
            }
        }
        mail++;
    }

    Save_Set(post_office.mail_recipient_flags, recipient_flags);
    Save_Set(post_office.keep_mail_sum_players, keep_mail_player_num);
    Save_Set(post_office.keep_mail_sum_npcs, 0);
}

static int mPO_receipt_check_mail(Mail_c* mail) {
    int res = FALSE;

    switch (mail->header.recipient.type) {
        case mMl_NAME_TYPE_PLAYER: {
            int house_idx = mMl_hunt_for_send_address(mail);
            if (house_idx != -1 && mPO_count_mail(house_idx) < HOME_MAILBOX_SIZE) {
                res = mPO_keep_contents(mail);
                if (res == TRUE) {
                    Save_Get(post_office.mail_recipient_flags) |= (1 << house_idx);
                    Save_Get(post_office.keep_mail_sum_players)++;
                }
            }

            break;
        }

        case mMl_NAME_TYPE_NPC: {
            if (mNpc_ReceiveHPMail(mail) == FALSE) {
                mNpc_SendMailtoNpc(mail);
            }

            res = mPO_keep_contents(mail);
            if (res == TRUE) {
                Save_Get(post_office).keep_mail_sum_npcs++;
            }

            break;
        }

        case mMl_NAME_TYPE_MUSEUM: {
            mMsm_SendMuseumMail(mail);
            mMl_clear_mail(mail);

            Save_Get(post_office).keep_mail_sum_npcs++;
            res = TRUE;

            break;
        }
    }

    return res;
}

extern int mPO_receipt_proc(Mail_c* mail, int send_type) {
    int res = FALSE;

    switch (send_type) {
        case mPO_SENDTYPE_MAIL: {
            res = mPO_receipt_check_mail(mail);
            break;
        }

        case mPO_SENDTYPE_LEAFLET: {
            mMl_copy_mail(&Save_Get(post_office).leaflet, mail);
            Save_Set(post_office.leaflet_recipient_flags.leaflet_flags, 0);
            res = TRUE;
            break;
        }

        case mPO_SENDTYPE_EVENT_LEAFLET: {
            mMl_copy_mail(&Save_Get(post_office).event_leaflet, mail);
            Save_Set(post_office.leaflet_recipient_flags.event_flags, 0);
            res = TRUE;
            break;
        }
    }

    return res;
}

static int mPO_copy_contents(int house_no, Mail_c* mail) {
    int res = FALSE;
    int free_slot = mMl_chk_mail_free_space(Save_Get(homes[house_no]).mailbox, HOME_MAILBOX_SIZE);

    if (free_slot >= 0) {
        mMl_copy_mail(Save_Get(homes[house_no]).mailbox + free_slot, mail);
        res = TRUE;
    }

    return res;
}

static void mPO_delivery_mail_with_item(Mail_c* mail, int mail_no, mActor_name_t present, PersonalID_c* pid) {
    int header_back_start;

    mHandbill_Load_HandbillFromRom(mail->content.header, &header_back_start, mail->content.footer, mail->content.body,
                                   mail_no);

    mail->content.font = mMl_FONT_RECV;
    mail->content.header_back_start = header_back_start;
    mail->content.mail_type = 7;
    mail->content.paper_type = ITM_PAPER55;
    mail->present = present;

    mMl_set_to_plname(mail, pid);
}

static int mPO_delivery_mail_with_order_ftr_sub(int house_no, PersonalID_c* pid, mPr_catalog_order_c* order) {
    Mail_c mail;
    u8 item_name[mIN_ITEM_NAME_LEN];

    mMl_clear_mail(&mail);
    mIN_copy_name_str(item_name, order->item);
    mHandbill_Set_free_str(0, item_name, mIN_ITEM_NAME_LEN);
    mPO_delivery_mail_with_item(&mail, 0x049 + order->shop_level, order->item, pid);
    return mPO_copy_contents(house_no, &mail);
}

static void mPO_delivery_mail_with_order_ftr(int house_no, int player_no) {
    Private_c* priv = Save_GetPointer(private_data[player_no]);

    if (priv->exists == TRUE) {
        mPr_catalog_order_c* order = priv->catalog_orders;
        int i;

        for (i = 0; i < mPr_CATALOG_ORDER_NUM; i++) {
            if (order->item != EMPTY_NO) {
                if (mPO_delivery_mail_with_order_ftr_sub(house_no, &priv->player_ID, order) == FALSE) {
                    return;
                }

                order->item = EMPTY_NO;
            }

            order++;
        }
    }
}

static void mPO_delivery_mail_with_ticket_set_free_str(lbRTC_month_t month) {
    u8 month_str[9];
    u8 day_str[4];

    int days = lbRTC_GetDaysByMonth(Common_Get(time.rtc_time.year), month);
    mString_Load_MonthStringFromRom(month_str, month);
    mString_Load_DayStringFromRom(day_str, days);
    mHandbill_Set_free_str(4, month_str, 9);
    mHandbill_Set_free_str(5, day_str, 4);
}

static int mPO_delivery_mail_with_ticket_sub(int house_no, PersonalID_c* pid, mActor_name_t present) {
    Mail_c mail;

    mMl_clear_mail(&mail);
    mPO_delivery_mail_with_item(&mail, 0x057, present, pid);
    return mPO_copy_contents(house_no, &mail);
}

static void mPO_delivery_mail_with_ticket(int house_no, int player_no) {
    mActor_name_t ticket;
    Private_c* priv = Save_GetPointer(private_data[player_no]);

    if (priv->exists == TRUE) {
        int ticket_num = priv->inventory.lotto_ticket_mail_storage;

        if (ticket_num != 0) {
            int minus_ticket;
            mActor_name_t ticket_id;
            lbRTC_month_t ticket_month = priv->inventory.lotto_ticket_expiry_month;

            mPO_delivery_mail_with_ticket_set_free_str(ticket_month);
            ticket = ITM_TICKET_START + (ticket_month - 1) * 8;

            for (ticket_num; ticket_num != 0; ticket_num -= minus_ticket) {
                minus_ticket = ticket_num < 5 ? ticket_num : 5;
                ticket_id = ticket + minus_ticket - 1;
                if (mPO_delivery_mail_with_ticket_sub(house_no, &priv->player_ID, ticket_id) == FALSE) {
                    break;
                }
            }
        }

        priv->inventory.lotto_ticket_mail_storage = ticket_num;
    }
}

static int mPO_delivery_mail_sub(int house_no) {
    PostOffice_c* post_office;
    Mail_c* po_mail;
    int i;
    int res = TRUE;

    po_mail = Save_Get(post_office).mail;
    post_office = Save_GetPointer(post_office);

    for (i = 0; i < mPO_MAIL_STORAGE_SIZE; i++, po_mail++) {
        if (mMl_check_not_used_mail(po_mail) == FALSE) {
            int dst_house_no = mMl_hunt_for_send_address(po_mail);
            if (dst_house_no != -1) {
                if (house_no == mPO_DELIVER_ALL_HOUSES || dst_house_no == house_no) {
                    if (mPO_copy_contents(dst_house_no, po_mail) == TRUE) {
                        post_office->keep_mail_sum_players--;
                        mMl_clear_mail(po_mail);
                    } else {
                        res = FALSE; // failed to deliver the mail, house mailbox is likely full
                    }
                }
            } else {
                switch (po_mail->header.recipient.type) {
                    case mMl_NAME_TYPE_PLAYER:
                        post_office->keep_mail_sum_players--;
                        break;
                }

                mMl_clear_mail(po_mail);
            }
        }
    }

    Save_Set(post_office.keep_mail_sum_npcs, 0);
    return res;
}

static void mPO_delivery_one_address_mail(int house_no) {
    if (mPO_delivery_mail_sub(house_no) == TRUE) {
        s16 mask = (1 << house_no) ^ 0b1111;
        Save_Get(post_office.mail_recipient_flags) &= mask; // clear out delivered houses
    }
}

static void mPO_delivery_one_address_special_mail(int house_no) {
    int player_no = mHS_get_pl_no(house_no);

    mPO_delivery_mail_with_ticket(house_no, player_no);
    mPO_delivery_mail_with_order_ftr(house_no, player_no);
}

static void mPO_delivery_one_address_leaflet(s16* leaflet_received_flags, Mail_c* leaflet, int house_no) {
    if ((((*leaflet_received_flags) >> house_no) & 1) == FALSE) {
        mHm_hs_c* house = Save_GetPointer(homes[house_no]);

        if (house->ownerID.land_id == 0xFFFF) {
            *leaflet_received_flags |= (1 << house_no); // unclaimed houses are marked "received"
        } else {
            int player_no = mHS_get_pl_no(house_no);
            if (player_no < mPr_FOREIGNER && mEv_ArbeitPlayer(player_no) == FALSE) {
                mMl_set_to_plname(leaflet, &house->ownerID);
                if (mPO_copy_contents(house_no, leaflet) == TRUE) {
                    *leaflet_received_flags |= (1 << house_no);
                }
            }
        }
    }
}

static void mPO_delivery_one_address_event_leaflet(int house_no) {
    Mail_c* ev_leaflet = &Save_Get(post_office).event_leaflet;
    int special_type = mEv_get_special_event_type();
    int leaflet_event_type;

    switch (ev_leaflet->content.mail_type) {
        case mMl_TYPE_SHOP_SALE_LEAFLET:
            leaflet_event_type = mEv_EVENT_SHOP_SALE;
            break;

        case mMl_TYPE_BROKER_SALE_LEAFLET:
            leaflet_event_type = mEv_EVENT_BROKER_SALE;
            break;

        default:
            leaflet_event_type = -1;
            break;
    }

    if (leaflet_event_type == special_type) {
        mPO_delivery_one_address_leaflet(&Save_Get(post_office).leaflet_recipient_flags.event_flags, ev_leaflet,
                                         house_no);
    }
}

extern int mPO_delivery_one_address(int house_no) {
    mPO_delivery_one_address_mail(house_no);
    mPO_delivery_one_address_leaflet(&Save_Get(post_office).leaflet_recipient_flags.leaflet_flags,
                                     &Save_Get(post_office).leaflet, house_no);
    mPO_delivery_one_address_event_leaflet(house_no);
}

static void mPO_delivery_mail() {
    mPO_delivery_mail_sub(mPO_DELIVER_ALL_HOUSES);
    mPO_adjust_keep_mail();
}

static void mPO_delivery_leaflet() {
    int house_no;

    for (house_no = 0; house_no < mHS_HOUSE_NUM; house_no++) {
        mPO_delivery_one_address_leaflet(&Save_Get(post_office).leaflet_recipient_flags.leaflet_flags,
                                         &Save_Get(post_office).leaflet, house_no);
        mPO_delivery_one_address_event_leaflet(house_no);
    }
}

extern void mPO_delivery_all_address_proc() {
    mPO_delivery_mail();
    mPO_delivery_leaflet();
}

static int mPO_make_post_man(GAME_PLAY* play) {
    static int bx_add[2] = { -1, 1 };
    static int ux_table[2] = { UT_X_NUM - 1, 0 };

    int pl_block_x;
    int pl_block_z;
    u32 block_kind;

    int po_block_x;
    int po_block_z;
    int res = FALSE;

    if (Save_Get(scene_no) == SCENE_FG) {
        mFI_BlockKind2BkNum(&pl_block_x, &pl_block_z, mRF_BLOCKKIND_PLAYER);
        block_kind = mFI_BkNum2BlockKind(play->block_table.block_x, play->block_table.block_z);

        if (block_kind != mRF_BLOCKKIND_NONE) {
            if ((block_kind & mRF_BLOCKKIND_PLAYER) != 0) {
                int bx_add_idx;
                int spawned_postman;

                mFI_BlockKind2BkNum(&po_block_x, &po_block_z, mRF_BLOCKKIND_POSTOFFICE);
                bx_add_idx = pl_block_x >= po_block_x ? 0 : 1;
                spawned_postman = (*Common_Get(clip).npc_clip->setupActor_proc)(
                    play, SP_NPC_POST_MAN, -1, -1, 0, play->block_table.block_x + bx_add[bx_add_idx],
                    play->block_table.block_z, ux_table[bx_add_idx], 7);

                if (spawned_postman == TRUE) {
                    res = TRUE;
                }
            } else if ((block_kind & mRF_BLOCKKIND_POSTOFFICE) != 0) {
                if (Common_Get(force_mail_delivery_flag) == TRUE) {
                    int post_utx;
                    int post_utz;
                    int spawned_postman;
                    ACTOR* post_office_structure =
                        Actor_info_fgName_search(&play->actor_info, POST_OFFICE, ACTOR_PART_ITEM);

                    if (post_office_structure != NULL) {
                        mFI_Wpos2UtNum_inBlock(&post_utx, &post_utz, post_office_structure->world.position);
                        spawned_postman = (*Common_Get(clip).npc_clip->setupActor_proc)(
                            play, SP_NPC_POST_MAN, -1, -1, 1, play->block_table.block_x, play->block_table.block_z,
                            post_utx - 3, post_utz);

                        if (spawned_postman == TRUE) {
                            res = TRUE;
                        }
                    }
                }
            } else {
                mPO_delivery_all_address_proc();
                res = TRUE;
            }
        } else {
            mPO_delivery_all_address_proc();
            res = TRUE;
        }
    } else {
        mPO_delivery_all_address_proc();
        res = TRUE;
    }

    return res;
}

static void mPO_set_next_delivery_time(lbRTC_time_c* time) {
    if (time->hour < 9) {
        time->hour = 9;
    } else if (time->hour < 17) {
        time->hour = 17;
    } else {
        lbRTC_Add_DD(time, 1);
        time->hour = 9;
    }

    time->min = 0;
    time->sec = 0;
    lbRTC_TimeCopy(&Save_Get(post_office).delivery_time, time);
}

static void mPO_delivery_proc(GAME_PLAY* play) {
    lbRTC_time_c time;

    lbRTC_TimeCopy(&time, Common_GetPointer(time.rtc_time));

    if ((lbRTC_IsOverTime(&Save_Get(post_office).delivery_time, &time) == lbRTC_OVER ||
         Common_Get(force_mail_delivery_flag) == TRUE) &&
        (mPO_make_post_man(play) == TRUE)) {
        mPO_set_next_delivery_time(&time);
        Common_Set(force_mail_delivery_flag, FALSE);
    }
}

static void mPO_first_delivery_proc() {
    int deliver = FALSE;
    lbRTC_time_c time;

    lbRTC_TimeCopy(&time, Common_GetPointer(time.rtc_time));

    if (lbRTC_IsOverTime(&Save_Get(post_office).delivery_time, &time) == lbRTC_OVER ||
        Common_Get(force_mail_delivery_flag) == TRUE) {
        deliver = TRUE;
        mPO_set_next_delivery_time(&time);
        Common_Set(force_mail_delivery_flag, FALSE);
    }

    if (deliver == TRUE) {
        mPO_delivery_mail();
    }

    if (mLd_PlayerManKindCheck() == FALSE) {
        int house_no = mHS_get_arrange_idx(Common_Get(player_no));
        mPO_delivery_one_address_special_mail(house_no);
    }

    if (deliver == TRUE) {
        mPO_delivery_leaflet();
    }
}

extern void mPO_business_proc(GAME_PLAY* play) {
    if ((Common_Get(clip).demo_clip == NULL && Common_Get(clip).demo_clip2 == NULL) ||
        (Common_Get(clip).demo_clip != NULL && Common_Get(clip).demo_clip->type == mDemo_CLIP_TYPE_INTRO_DEMO)) {
        int scene = Save_Get(scene_no);

        switch (scene) {
            default:
                mPO_delivery_proc(play);
                break;

            case SCENE_PLAYERSELECT:
            case SCENE_PLAYERSELECT_2:
            case SCENE_PLAYERSELECT_3:
            case SCENE_PLAYERSELECT_SAVE:
            case SCENE_TITLE_DEMO:
                return;
        }
    }
}

extern void mPO_first_work() {
    lbRTC_time_c* time = &Save_Get(save_check).time;
    if (mTM_AreTimesEqual(time, &mTM_rtcTime_clear_code) == FALSE && lbRTC_IsOverRTC(time) == FALSE) {
        Common_Set(force_mail_delivery_flag, TRUE);
    }

    mPO_first_delivery_proc();
    mPO_adjust_keep_mail();
}

extern void mPO_post_office_init() {
    bzero(Save_GetPointer(post_office), sizeof(PostOffice_c));
    mMl_clear_mail_box(Save_Get(post_office).mail, mPO_MAIL_STORAGE_SIZE);
    mMl_clear_mail(&Save_Get(post_office).leaflet);
    mMl_clear_mail(&Save_Get(post_office).event_leaflet);
    Save_Get(post_office).leaflet_recipient_flags.raw = 0x000F000F;
    lbRTC_TimeCopy(&Save_Get(post_office).delivery_time, Common_GetPointer(time.rtc_time));
}
