#include "global.h"
#include "main.h"
#include "battle.h"
#include "battle_records.h"
#include "battle_setup.h"
#include "cable_club.h"
#include "data2.h"
#include "event_data.h"
#include "field_message_box.h"
#include "field_specials.h"
#include "field_weather.h"
#include "international_string_util.h"
#include "link.h"
#include "link_rfu.h"
#include "load_save.h"
#include "m4a.h"
#include "menu.h"
#include "overworld.h"
#include "palette.h"
#include "rom_8011DC0.h"
#include "script.h"
#include "script_pokemon_util_80F87D8.h"
#include "sound.h"
#include "start_menu.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "trade.h"
#include "trainer_card.h"
#include "party_menu.h"
#include "window.h"
#include "constants/songs.h"

static const struct WindowTemplate gUnknown_08550594 = {
    .bg = 0,
    .tilemapLeft = 16,
    .tilemapTop = 11,
    .width = 11,
    .height = 2,
    .paletteNum = 15,
    .baseBlock = 0x0125,
};
static const u8 *const gTrainerCardColorNames[] = {
    gText_BronzeCard,
    gText_CopperCard,
    gText_SilverCard,
    gText_GoldCard
};

static void sub_80B2634(u8 taskId);
static void sub_80B2688(u8 taskId);
static void sub_80B270C(u8 taskId);
static void sub_80B275C(u8 taskId);
static void sub_80B2804(u8 taskId);
static void sub_80B28A8(u8 taskId);
static void sub_80B2918(u8 taskId);
static void sub_80B2A08(u8 taskId);
static void sub_80B2C30(u8 taskId);
static void sub_80B2CB0(u8 taskId);
static void sub_80B2CEC(u8 taskId);
static void sub_80B2D2C(u8 taskId);
static bool8 sub_80B2D6C(u8 taskId);
static void sub_80B2EE4(u8 taskId);
static void sub_80B3144(u8 taskId);
static void sub_80B3194(u8 taskId);
static void sub_80B31E8(u8 taskId);
static void sub_80B3220(u8 taskId);

static void sub_80B236C(u8 arg0, u8 arg1)
{
    if (FindTaskIdByFunc(sub_80B2634) == 0xFF)
    {
        u8 taskId1;

        taskId1 = CreateTask(sub_80B2634, 80);
        gTasks[taskId1].data[1] = arg0;
        gTasks[taskId1].data[2] = arg1;
    }
}

static void sub_80B23B0(u16 windowId, u32 value)
{
    u8 xPos;

    ConvertIntToDecimalStringN(gStringVar1, value, STR_CONV_MODE_LEFT_ALIGN, 1);
    SetStandardWindowBorderStyle(windowId, 0);
    StringExpandPlaceholders(gStringVar4, gText_XPLink);
    xPos = GetStringCenterAlignXOffset(1, gStringVar4, 88);
    AddTextPrinterParameterized(windowId, 1, gStringVar4, xPos, 1, 0xFF, NULL);
    CopyWindowToVram(windowId, 3);
}

static void sub_80B241C(u16 windowId)
{
    sub_819746C(windowId, FALSE);
    CopyWindowToVram(windowId, 3);
}

static void sub_80B243C(u8 taskId, u8 arg1)
{
    s16 *data = gTasks[taskId].data;

    if (arg1 != data[3])
    {
        if (arg1 <= 1)
            sub_80B241C(data[5]);
        else
            sub_80B23B0(data[5], arg1);
        data[3] = arg1;
    }
}

static u32 sub_80B2478(u8 lower, u8 upper)
{
    int playerCount;

    switch (GetLinkPlayerDataExchangeStatusTimed(lower, upper))
    {
    case EXCHANGE_COMPLETE:
        return 1;
    case EXCHANGE_IN_PROGRESS:
        return 3;
    case EXCHANGE_STAT_4:
        return 7;
    case EXCHANGE_STAT_5:
        return 9;
    case EXCHANGE_STAT_6:
        ConvertIntToDecimalStringN(gStringVar1, GetLinkPlayerCount_2(), STR_CONV_MODE_LEFT_ALIGN, 1);
        return 4;
    case EXCHANGE_STAT_7:
        return 10;
    case EXCHANGE_TIMED_OUT:
    default:
        return 0;
    }
}

static bool32 sub_80B24F8(u8 taskId)
{
    if (HasLinkErrorOccurred() == TRUE)
    {
        gTasks[taskId].func = sub_80B2D2C;
        return TRUE;
    }
    return FALSE;
}

static bool32 sub_80B252C(u8 taskId)
{
    if ((gMain.newKeys & B_BUTTON)
     && IsLinkConnectionEstablished() == FALSE)
    {
        gLinkType = 0;
        gTasks[taskId].func = sub_80B2CEC;
        return TRUE;
    }
    return FALSE;
}

static bool32 sub_80B2578(u8 taskId)
{
    if (IsLinkConnectionEstablished())
        SetSuppressLinkErrorMessage(TRUE);

    if (gMain.newKeys & B_BUTTON)
    {
        gLinkType = 0;
        gTasks[taskId].func = sub_80B2CEC;
        return TRUE;
    }
    return FALSE;
}

static bool32 sub_80B25CC(u8 taskId)
{
    if (GetSioMultiSI() == 1)
    {
        gTasks[taskId].func = sub_80B2D2C;
        return TRUE;
    }
    return FALSE;
}

static void sub_80B2600(u8 taskId)
{
    gTasks[taskId].data[0]++;
    if (gTasks[taskId].data[0] == 10)
    {
        sub_800A4D8(2);
        DestroyTask(taskId);
    }
}

static void sub_80B2634(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    if (data[0] == 0)
    {
        OpenLinkTimed();
        sub_800AB98();
        ResetLinkPlayers();
        data[5] = AddWindow(&gUnknown_08550594);
    }
    else if (data[0] > 9)
    {
        gTasks[taskId].func = sub_80B2688;
    }
    data[0]++;
}

static void sub_80B2688(u8 taskId)
{
    u32 playerCount = GetLinkPlayerCount_2();

    if (sub_80B252C(taskId) == TRUE
     || sub_80B2578(taskId) == TRUE
     || playerCount < 2)
        return;

    SetSuppressLinkErrorMessage(TRUE);
    gTasks[taskId].data[3] = 0;
    if (IsLinkMaster() == TRUE)
    {
        PlaySE(SE_PIN);
        ShowFieldAutoScrollMessage(gText_ConfirmLinkWhenPlayersReady);
        gTasks[taskId].func = sub_80B270C;
    }
    else
    {
        PlaySE(SE_BOO);
        ShowFieldAutoScrollMessage(gText_AwaitingLinkup);
        gTasks[taskId].func = sub_80B2918;
    }
}

static void sub_80B270C(u8 taskId)
{
    if (sub_80B252C(taskId) == TRUE
     || sub_80B25CC(taskId) == TRUE
     || sub_80B24F8(taskId) == TRUE)
        return;

    if (GetFieldMessageBoxMode() == FIELD_MESSAGE_BOX_HIDDEN)
    {
        gTasks[taskId].data[3] = 0;
        gTasks[taskId].func = sub_80B275C;
    }
}

static void sub_80B275C(u8 taskId)
{
    s16 *data = gTasks[taskId].data;
    s32 linkPlayerCount = GetLinkPlayerCount_2();

    if (sub_80B252C(taskId) == TRUE
     || sub_80B25CC(taskId) == TRUE
     || sub_80B24F8(taskId) == TRUE)
        return;

    sub_80B243C(taskId, linkPlayerCount);

    if (!(gMain.newKeys & A_BUTTON))
        return;

    if (linkPlayerCount < data[1])
        return;

    sub_800AA04(linkPlayerCount);
    sub_80B241C(data[5]);
    ConvertIntToDecimalStringN(gStringVar1, linkPlayerCount, STR_CONV_MODE_LEFT_ALIGN, 1);
    ShowFieldAutoScrollMessage(gText_ConfirmStartLinkWithXPlayers);
    gTasks[taskId].func = sub_80B2804;
}

static void sub_80B2804(u8 taskId)
{
    if (sub_80B252C(taskId) == TRUE
     || sub_80B25CC(taskId) == TRUE
     || sub_80B24F8(taskId) == TRUE)
        return;

    if (GetFieldMessageBoxMode() == FIELD_MESSAGE_BOX_HIDDEN)
    {
        if (sub_800AA48() != GetLinkPlayerCount_2())
        {
            ShowFieldAutoScrollMessage(gText_ConfirmLinkWhenPlayersReady);
            gTasks[taskId].func = sub_80B270C;
        }
        else if (gMain.heldKeys & B_BUTTON)
        {
            ShowFieldAutoScrollMessage(gText_ConfirmLinkWhenPlayersReady);
            gTasks[taskId].func = sub_80B270C;
        }
        else if (gMain.heldKeys & A_BUTTON)
        {
            PlaySE(SE_SELECT);
            sub_800A620();
            gTasks[taskId].func = sub_80B28A8;
        }
    }
}

static void sub_80B28A8(u8 taskId)
{
    u8 local1 = gTasks[taskId].data[1];
    u8 local2 = gTasks[taskId].data[2];

    if (sub_80B24F8(taskId) == TRUE
     || sub_80B2D6C(taskId) == TRUE)
        return;

    if (GetLinkPlayerCount_2() != sub_800AA48())
    {
        gTasks[taskId].func = sub_80B2D2C;
    }
    else
    {
        gSpecialVar_Result = sub_80B2478(local1, local2);
        if (gSpecialVar_Result != 0)
            gTasks[taskId].func = sub_80B2A08;
    }
}

static void sub_80B2918(u8 taskId)
{
    u8 local1, local2;
    struct TrainerCard *card;

    local1 = gTasks[taskId].data[1];
    local2 = gTasks[taskId].data[2];

    if (sub_80B252C(taskId) == TRUE
     || sub_80B24F8(taskId) == TRUE)
        return;

    gSpecialVar_Result = sub_80B2478(local1, local2);
    if (gSpecialVar_Result == 0)
        return;
    if (gSpecialVar_Result == 3 || gSpecialVar_Result == 4)
    {
        sub_800AC34();
        HideFieldMessageBox();
        gTasks[taskId].func = sub_80B2CB0;
    }
    else if (gSpecialVar_Result == 7 || gSpecialVar_Result == 9)
    {
        CloseLink();
        HideFieldMessageBox();
        gTasks[taskId].func = sub_80B2CB0;
    }
    else
    {
        gFieldLinkPlayerCount = GetLinkPlayerCount_2();
        gUnknown_03005DB4 = GetMultiplayerId();
        sub_800AA04(gFieldLinkPlayerCount);
        card = (struct TrainerCard *)gBlockSendBuffer;
        TrainerCard_GenerateCardForPlayer(card);
        card->monSpecies[0] = GetMonData(&gPlayerParty[gSelectedOrderFromParty[0] - 1], MON_DATA_SPECIES, NULL);
        card->monSpecies[1] = GetMonData(&gPlayerParty[gSelectedOrderFromParty[1] - 1], MON_DATA_SPECIES, NULL);
        gTasks[taskId].func = sub_80B2C30;
    }
}

static void sub_80B2A08(u8 taskId)
{
    struct TrainerCard *card;

    if (sub_80B24F8(taskId) == TRUE)
        return;

    if (gSpecialVar_Result == 4)
    {
        if (!Link_AnyPartnersPlayingRubyOrSapphire())
        {
            sub_800AC34();
            HideFieldMessageBox();
            gTasks[taskId].func = sub_80B2CB0;
        }
        else
        {
            CloseLink();
            HideFieldMessageBox();
            gTasks[taskId].func = sub_80B2CB0;
        }
    }
    else if (gSpecialVar_Result == 3)
    {
        sub_800AC34();
        HideFieldMessageBox();
        gTasks[taskId].func = sub_80B2CB0;
    }
    else if (gSpecialVar_Result == 7 || gSpecialVar_Result == 9)
    {
        CloseLink();
        HideFieldMessageBox();
        gTasks[taskId].func = sub_80B2CB0;
    }
    else
    {
        gFieldLinkPlayerCount = GetLinkPlayerCount_2();
        gUnknown_03005DB4 = GetMultiplayerId();
        sub_800AA04(gFieldLinkPlayerCount);
        card = (struct TrainerCard *)gBlockSendBuffer;
        TrainerCard_GenerateCardForPlayer(card);
        card->monSpecies[0] = GetMonData(&gPlayerParty[gSelectedOrderFromParty[0] - 1], MON_DATA_SPECIES, NULL);
        card->monSpecies[1] = GetMonData(&gPlayerParty[gSelectedOrderFromParty[1] - 1], MON_DATA_SPECIES, NULL);
        gTasks[taskId].func = sub_80B2C30;
        sub_800A4D8(2);
    }
}

bool32 sub_80B2AF4(u16 *arg0, u16 *arg1)
{
    int i;
    int j;
    bool32 result = FALSE;
    int k = 0;

    gStringVar1[0] = EOS;
    gStringVar2[0] = EOS;

    for (i = 0; i < 2; i++)
    {
        for (j = 0; j < 2; j++)
        {
            if (arg0[i] == arg1[j])
            {
                if (k == 0)
                {
                    StringCopy(gStringVar1, gSpeciesNames[arg0[i]]);
                    result = TRUE;
                }

                if (k == 1)
                {
                    StringCopy(gStringVar2, gSpeciesNames[arg0[i]]);
                    result = TRUE;
                }

                k++;
            }
        }
    }

    gSpecialVar_0x8005 = k;

    return result;
}

static void task_map_chg_seq_0807EC34(u16 *a0, u32 taskId)
{
    struct TrainerCard *trainerCards = gTrainerCards;

    if (*a0 == 1)
    {
        if (gLinkType == 0x2266 || gLinkType == 0x2277)
        {
            if (sub_80B2AF4(trainerCards[0].monSpecies, trainerCards[1].monSpecies))
            {
                *a0 = 11;
                sub_800AC34();
                gTasks[taskId].func = sub_80B2CB0;
            }
            else
            {
                sub_80B241C(gTasks[taskId].data[5]);
                EnableBothScriptContexts();
                DestroyTask(taskId);
            }
        }
        else
        {
            sub_80B241C(gTasks[taskId].data[5]);
            EnableBothScriptContexts();
            DestroyTask(taskId);
        }
    }
    else
    {
        sub_800AC34();
        gTasks[taskId].func = sub_80B2CB0;
    }
}

static void sub_80B2C30(u8 taskId)
{
    u8 index;
    struct TrainerCard *trainerCards;

    if (sub_80B24F8(taskId) == TRUE)
        return;

    if (GetBlockReceivedStatus() != sub_800A9A8())
        return;

    for (index = 0; index < GetLinkPlayerCount(); index++)
    {
        CopyTrainerCardData(&gTrainerCards[index], gBlockRecvBuffer[index], gLinkPlayers[index].version);
    }

    SetSuppressLinkErrorMessage(FALSE);
    ResetBlockReceivedFlags();
    task_map_chg_seq_0807EC34(&gSpecialVar_Result, taskId);
}

static void sub_80B2CB0(u8 taskId)
{
    if (gReceivedRemoteLinkPlayers == FALSE)
    {
        sub_80B241C(gTasks[taskId].data[5]);
        EnableBothScriptContexts();
        RemoveWindow(gTasks[taskId].data[5]);
        DestroyTask(taskId);
    }
}

static void sub_80B2CEC(u8 taskId)
{
    gSpecialVar_Result = 5;
    sub_80B241C(gTasks[taskId].data[5]);
    sub_8098374();
    RemoveWindow(gTasks[taskId].data[5]);
    EnableBothScriptContexts();
    DestroyTask(taskId);
}

static void sub_80B2D2C(u8 taskId)
{
    gSpecialVar_Result = 6;
    sub_80B241C(gTasks[taskId].data[5]);
    RemoveWindow(gTasks[taskId].data[5]);
    HideFieldMessageBox();
    EnableBothScriptContexts();
    DestroyTask(taskId);
}

static bool8 sub_80B2D6C(u8 taskId)
{
    gTasks[taskId].data[4]++;
    if (gTasks[taskId].data[4] > 600)
    {
        gTasks[taskId].func = sub_80B2D2C;
        return TRUE;
    }

    return FALSE;
}

void sub_80B2DA4(u8 arg0)
{
    u8 r3 = 2;
    u8 r2 = 2;

    switch (gSpecialVar_0x8004)
    {
    case 1:
        r3 = 2;
        gLinkType = 0x2233;
        break;
    case 2:
        r3 = 2;
        gLinkType = 0x2244;
        break;
    case 5:
        r3 = 4;
        r2 = 4;
        gLinkType = 0x2255;
        break;
    case 9:
        r3 = 2;
        if (gSaveBlock2Ptr->frontier.lvlMode == 0)
        {
            gLinkType = 0x2266;
        }
        else
        {
            gLinkType = 0x2277;
        }
        break;
    }

    sub_80B236C(r3, r2);
}

void sub_80B2E4C(void)
{
    gLinkType = 0x1133;
    gBattleTypeFlags = 0;
    sub_80B236C(2, 2);
}

void sub_80B2E74(void)
{
    gSpecialVar_Result = 0;
    gLinkType = 0x3311;
    gBattleTypeFlags = 0;
    sub_80B236C(2, 4);
}

void sub_80B2EA8(void)
{
    u32 taskId = FindTaskIdByFunc(sub_80B2EE4);

    if (taskId == 0xFF)
    {
        taskId = CreateTask(sub_80B2EE4, 80);
        gTasks[taskId].data[0] = 0;
    }
}

static void sub_80B2EE4(u8 taskId)
{
    int playerCount;
    int i;

    switch (gTasks[taskId].data[0])
    {
    case 0:
        if (gSpecialVar_Result == 1)
        {
            bool32 unk = FALSE;
            bool32 isEnglishRSLinked = FALSE;
            bool32 isJapaneseEmeraldLinked = FALSE;

            playerCount = GetLinkPlayerCount();
            for (i = 0; i < playerCount; i++)
            {
                u32 version = (u8)gLinkPlayers[i].version;
                u32 language = gLinkPlayers[i].language;

                if (version == VERSION_RUBY || version == VERSION_SAPPHIRE)
                {
                    if (language == LANGUAGE_JAPANESE)
                    {
                        unk = TRUE;
                        break;
                    }
                    else
                    {
                        isEnglishRSLinked = TRUE;
                    }
                }
                else if (version == VERSION_EMERALD)
                {
                    if (language == LANGUAGE_JAPANESE)
                    {
                        isJapaneseEmeraldLinked = TRUE;
                    }
                }
            }

            if (isEnglishRSLinked && isJapaneseEmeraldLinked)
            {
                unk = TRUE;
            }

            if (unk)
            {
                gSpecialVar_Result = 12;
                sub_800AD10();
                gTasks[taskId].data[0] = 1;
                return;
            }
        }
        EnableBothScriptContexts();
        DestroyTask(taskId);
        break;
    case 1:
        if (gReceivedRemoteLinkPlayers == FALSE)
        {
            EnableBothScriptContexts();
            DestroyTask(taskId);
        }
        break;
    }
}

void sub_80B2FD8(void)
{
    gLinkType = 0x4411;
    gBattleTypeFlags = 0;
    sub_80B236C(2, 4);
}

void sub_80B3000(void)
{
    gLinkType = 0x6601;
    gBattleTypeFlags = 0;
    sub_80B236C(4, 4);
}

void sub_80B3028(void)
{
    gLinkType = 0x6602;
    gBattleTypeFlags = 0;
    sub_80B236C(2, 4);
}

u8 sub_80B3050(void)
{
    if (FuncIsActiveTask(sub_80B3144) != FALSE)
        return 0xFF;

    switch (gSpecialVar_0x8004)
    {
    case 1:
        gLinkType = 0x2233;
        break;
    case 2:
        gLinkType = 0x2244;
        break;
    case 5:
        gLinkType = 0x2255;
        break;
    case 9:
        if (gSaveBlock2Ptr->frontier.lvlMode == 0)
        {
            gLinkType = 0x2266;
        }
        else
        {
            gLinkType = 0x2277;
        }
        break;
    case 3:
        gLinkType = 0x1111;
        break;
    case 4:
        gLinkType = 0x3322;
        break;
    }

    return CreateTask(sub_80B3144, 80);
}

static void sub_80B3144(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    if (data[0] == 0)
    {
        OpenLink();
        ResetLinkPlayers();
        CreateTask(task00_08081A90, 80);
    }
    else if (data[0] >= 10)
    {
        gTasks[taskId].func = sub_80B3194;
    }
    data[0]++;
}

static void sub_80B3194(u8 taskId)
{
    if (GetLinkPlayerCount_2() >= 2)
    {
        if (IsLinkMaster() == TRUE)
            gTasks[taskId].func = sub_80B31E8;
        else
            gTasks[taskId].func = sub_80B3220;
    }
}

static void sub_80B31E8(u8 taskId)
{
    if (sub_800AA48() == GetLinkPlayerCount_2())
    {
        sub_800A620();
        gTasks[taskId].func = sub_80B3220;
    }
}

static void sub_80B3220(u8 taskId)
{
    if (gReceivedRemoteLinkPlayers == TRUE
     && IsLinkPlayerDataExchangeComplete() == TRUE)
    {
        sub_800AB18();
        sub_8009F18();
        DestroyTask(taskId);
    }
}

void sub_80B3254(void)
{
    SaveGame();
}

static void sub_80B3260(int a0)
{
    switch (a0)
    {
        case 1:
            gBattleTypeFlags = BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER;
            break;
        case 2:
            gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER;
            break;
        case 5:
            ReducePlayerPartyToSelectedMons();
            gBattleTypeFlags = BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER | BATTLE_TYPE_MULTI;
            break;
        case 9:
            gBattleTypeFlags = BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER | BATTLE_TYPE_MULTI;
            break;
    }
}

static void sub_80B32B4(u8 taskId)
{
    struct Task* task = &gTasks[taskId];

    switch (task->data[0])
    {
    case 0:
        FadeScreen(1, 0);
        gLinkType = 0x2211;
        ClearLinkCallback_2();
        task->data[0]++;
        break;
    case 1:
        if (!gPaletteFade.active)
            task->data[0]++;
        break;
    case 2:
        task->data[1]++;
        if (task->data[1] > 20)
            task->data[0]++;
        break;
    case 3:
        sub_800AC34();
        task->data[0]++;
        break;
    case 4:
        if (!gReceivedRemoteLinkPlayers)
            task->data[0]++;
        break;
    case 5:
        if (gLinkPlayers[0].trainerId & 1)
            PlayMapChosenOrBattleBGM(MUS_BATTLE32);
        else
            PlayMapChosenOrBattleBGM(MUS_BATTLE20);

        sub_80B3260(gSpecialVar_0x8004);
        CleanupOverworldWindowsAndTilemaps();
        gTrainerBattleOpponent_A = 0x800;
        SetMainCallback2(CB2_InitBattle);
        gMain.savedCallback = sub_80B360C;
        DestroyTask(taskId);
        break;
    }
}

static void sub_80B33BC(u8 taskId)
{
    int i;
    s16* data = gTasks[taskId].data;

    switch (data[0])
    {
    case 0:
        FadeScreen(1, 0);
        gLinkType = 0x2211;
        ClearLinkCallback_2();
        data[0] = 1;
        break;
    case 1:
        if (!gPaletteFade.active)
            data[0] = 2;
        break;
    case 2:
        SendBlock(0, &gLocalLinkPlayer, sizeof(gLocalLinkPlayer));
        data[0] = 3;
        break;
    case 3:
        if (GetBlockReceivedStatus() == sub_800A9D8())
        {
            for (i = 0; i < GetLinkPlayerCount(); i++)
            {
                struct LinkPlayer *player = (struct LinkPlayer *)gBlockRecvBuffer[i];
                gLinkPlayers[i] = *player;
                sub_800B524(&gLinkPlayers[i]);
                ResetBlockReceivedFlag(i);
            }
            data[0] = 4;
        }
        break;
    case 4:
        data[1]++;
        if (data[1] > 20)
            data[0] = 5;
        break;
    case 5:
        sub_800ADF8();
        data[0] = 6;
        break;
    case 6:
        if (IsLinkTaskFinished())
        {
            data[0] = 7;
        }
        break;
    case 7:
        if (gLinkPlayers[0].trainerId & 1)
            PlayMapChosenOrBattleBGM(MUS_BATTLE32);
        else
            PlayMapChosenOrBattleBGM(MUS_BATTLE20);

        gLinkPlayers[0].linkType = 0x2211;
        sub_80B3260(gSpecialVar_0x8004);
        CleanupOverworldWindowsAndTilemaps();
        gTrainerBattleOpponent_A = 0x800;
        SetMainCallback2(CB2_InitBattle);
        gMain.savedCallback = sub_80B360C;
        DestroyTask(taskId);
        break;
    }
}

static void sub_80B3554(void)
{
    u8 playerCount;
    int i;
    bool32 r4;

    switch (gMain.state)
    {
        case 0:
            playerCount = GetLinkPlayerCount();
            r4 = FALSE;
            for (i = 0; i < playerCount; i++)
            {
                u32 version = (u8)gLinkPlayers[i].version;
                if (version == VERSION_FIRE_RED || version == VERSION_LEAF_GREEN)
                {
                    r4 = TRUE;
                    break;
                }
            }

            if (r4)
            {
                gMain.state = 2;
            }
            else
            {
                sub_800AC34();
                gMain.state = 1;
            }
            break;
        case 1:
            if (gReceivedRemoteLinkPlayers == FALSE)
            {
                SetMainCallback2(CB2_ReturnToField);
            }
            break;
        case 2:
            SetMainCallback2(CB2_ReturnToField);
            break;
    }
    RunTasks();
}

void sub_80B360C(void)
{
    gBattleTypeFlags &= ~BATTLE_TYPE_20;
    Overworld_ResetMapMusic();
    LoadPlayerParty();
    SavePlayerBag();
    sub_813BF10();

    if (gSpecialVar_0x8004 == 1 || gSpecialVar_0x8004 == 2)
    {
        UpdatePlayerLinkBattleRecords(gUnknown_03005DB4 ^ 1);
        if (gWirelessCommType)
        {
            switch (gBattleOutcome)
            {
                case B_OUTCOME_WON:
                    sub_801B990(0, gLinkPlayers[GetMultiplayerId() ^ 1].trainerId);
                    break;
                case B_OUTCOME_LOST:
                    sub_801B990(1, gLinkPlayers[GetMultiplayerId() ^ 1].trainerId);
                    break;
            }
        }
    }

    if (InUnionRoom() == TRUE)
    {
        gMain.savedCallback = sub_80B3554;
    }
    else
    {
        gMain.savedCallback = c2_8056854;
    }

    SetMainCallback2(sub_80A0514);
}

void sub_80B36EC(void)
{
    if (gSpecialVar_0x8004 == 1 || gSpecialVar_0x8004 == 2 || gSpecialVar_0x8004 == 5 || gSpecialVar_0x8004 == 9)
    {
        LoadPlayerParty();
        SavePlayerBag();
    }
    SetWarpDestinationToDynamicWarp(0x7F);
}

void sub_80B371C(void)
{
    sub_80872B0();
}

static void sub_80B3728(u8 taskId)
{
    struct Task* task = &gTasks[taskId];

    switch (task->data[0])
    {
    case 0:
        ShowFieldMessage(gText_PleaseWaitForLink);
        task->data[0] = 1;
        break;
    case 1:
        if (IsFieldMessageBoxHidden())
        {
            sub_8087288();
            sub_8009628(gSpecialVar_0x8005);
            task->data[0] = 2;
        }
        break;
    case 2:
        switch (sub_8087214())
        {
        case 0:
            break;
        case 1:
            HideFieldMessageBox();
            task->data[0] = 0;
            sub_80872C4();
            SwitchTaskToFollowupFunc(taskId);
            break;
        case 2:
            task->data[0] = 3;
            break;
        }
        break;
    case 3:
        sub_808729C();
        sub_8197AE8(TRUE);
        DestroyTask(taskId);
        EnableBothScriptContexts();
        break;
    }
}

void sub_80B37D4(TaskFunc followupFunc)
{
    u8 taskId = CreateTask(sub_80B3728, 80);
    SetTaskFuncWithFollowupFunc(taskId, sub_80B3728, followupFunc);
    ScriptContext1_Stop();
}

static void sub_80B37FC(u8 taskId)
{
    struct Task *task = &gTasks[taskId];

    switch (task->data[0])
    {
    case 0:
        ScriptContext2_Enable();
        FadeScreen(1, 0);
        ClearLinkCallback_2();
        task->data[0]++;
        break;
    case 1:
        if (!gPaletteFade.active)
            task->data[0]++;
        break;
    case 2:
        gUnknown_02032298[0] = 0;
        gUnknown_02032298[1] = 0;
        m4aMPlayAllStop();
        sub_800AC34();
        task->data[0]++;
        break;
    case 3:
        if (!gReceivedRemoteLinkPlayers)
        {
            SetMainCallback2(sub_80773AC);
            DestroyTask(taskId);
        }
        break;
    }
}

static void sub_80B3894(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    switch (data[0])
    {
    case 0:
        ScriptContext2_Enable();
        FadeScreen(1, 0);
        Rfu_set_zero();
        data[0]++;
        break;
    case 1:
        if (!gPaletteFade.active)
            data[0]++;
        break;
    case 2:
        gUnknown_02032298[0] = 0;
        gUnknown_02032298[1] = 0;
        m4aMPlayAllStop();
        sub_800ADF8();
        data[0]++;
        break;
    case 3:
        if (IsLinkTaskFinished())
        {
            sub_8013F78();
            DestroyTask(taskId);
        }
        break;
    }
}

void sub_80B3924(void)
{
    if (gWirelessCommType != 0)
    {
        sub_80B37D4(sub_80B3894);
    }
    else
    {
        sub_80B37D4(sub_80B37FC);
    }
}

static void sub_80B3950(void)
{
    CreateTask(sub_80B37FC, 80);
}

void nullsub_37(void)
{

}

void sub_80B3968(void)
{
    gLinkType = 0x2211;

    if (gWirelessCommType != 0)
    {
        sub_80B37D4(sub_80B33BC);
    }
    else
    {
        sub_80B37D4(sub_80B32B4);
    }
}

static void sub_80B39A4(void)
{
    u8 taskId = CreateTask(sub_80B3728, 80);
    ScriptContext1_Stop();
}

void sp02A_crash_sound(void)
{
    ShowTrainerCardInLink(gSpecialVar_0x8006, CB2_ReturnToFieldContinueScriptPlayMapMusic);
}

bool32 sub_80B39D4(u8 linkPlayerIndex)
{
    u32 trainerCardColorIndex;

    gSpecialVar_0x8006 = linkPlayerIndex;
    StringCopy(gStringVar1, gLinkPlayers[linkPlayerIndex].name);

    trainerCardColorIndex = sub_80C4904(linkPlayerIndex);
    if (trainerCardColorIndex == 0)
        return FALSE;

    StringCopy(gStringVar2, gTrainerCardColorNames[trainerCardColorIndex - 1]);
    return TRUE;
}

void task00_08081A90(u8 taskId)
{
    struct Task *task = &gTasks[taskId];

    task->data[0]++;
    if (task->data[0] > 300)
    {
        CloseLink();
        SetMainCallback2(CB2_LinkError);
        DestroyTask(taskId);
    }

    if (gReceivedRemoteLinkPlayers)
    {
        if (gWirelessCommType == 0)
        {
            if (!sub_800AA60())
            {
                CloseLink();
                SetMainCallback2(CB2_LinkError);
            }
            DestroyTask(taskId);
        }
        else
        {
            DestroyTask(taskId);
        }
    }
}

static void sub_80B3AAC(u8 taskId)
{
    if (!gReceivedRemoteLinkPlayers)
    {
        EnableBothScriptContexts();
        DestroyTask(taskId);
    }
}

static void sub_80B3AD0(u8 taskId)
{
    sub_800AC34();
    gTasks[taskId].func = sub_80B3AAC;
}

void sub_80B3AF8(u8 taskId)
{
    s16 *data = gTasks[taskId].data;

    switch (data[0])
    {
        case 0:
            if (gWirelessCommType != 0)
            {
                DestroyTask(taskId);
            }
            else
            {
                OpenLink();
                CreateTask(task00_08081A90, 1);
                data[0]++;
            }
            break;
        case 1:
            if (++data[1] > 11)
            {
                data[1] = 0;
                data[0]++;
            }
            break;
        case 2:
            if (GetLinkPlayerCount_2() >= sub_800AA48())
            {
                if (IsLinkMaster())
                {
                    if (++data[1] > 30)
                    {
                        sub_800A620();
                        data[0]++;
                    }
                }
                else
                {
                    data[0]++;
                }
            }
            break;
        case 3:
            if (gReceivedRemoteLinkPlayers == TRUE && IsLinkPlayerDataExchangeComplete() == TRUE)
            {
                DestroyTask(taskId);
            }
            break;
    }
}

void sub_80B3BC4(void)
{
    if (gWirelessCommType == 0)
    {
        gLinkType = 0x2288;
    }
}
