/*---------------------------------------------------------------------------*
  Project:  NitroSDK - WM - demos - simple-1
  File:     main.c

  Copyright 2003,2004 Nintendo.  All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law.  They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Log: main.c,v $
  Revision 1.26  2004/11/08 02:47:11  takano_makoto
  Changed the return value of scanCallback. Added WH_EndScan.

  Revision 1.25  2004/11/05 09:16:48  sasakis
  Improved the behavior of windows, and arranged codes.

  Revision 1.24  2004/11/05 04:27:40  sasakis
  Added the lobby screen and parent selection screen, and made the necessary modifications (such as scan related).

  Revision 1.23  2004/11/02 07:12:55  sasakis
  Arranged codes.

  Revision 1.22  2004/11/02 01:07:34  sasakis
  Restored the portion that had became meaningless codes. Changed organization and such.

  Revision 1.21  2004/11/01 09:47:21  seiki_masashi
  Deleted unnecessary strings.

  Revision 1.20  2004/11/01 09:42:17  seiki_masashi
  Changed sSendBuf and sRecvBuf from 256 bytes to 512 bytes.

  Revision 1.19  2004/11/01 04:11:44  sasakis
  Added the process failure countermeasure code and such (provisional version that includes the currently working codes).

  Revision 1.18  2004/10/29 02:06:37  takano_makoto
  WH_ChildConnectAuto interface change

  Revision 1.17  2004/10/28 08:10:19  takano_makoto
  Changed the WH_Connect function name.

  Revision 1.16  2004/10/27 03:47:00  takano_makoto
  Changed MeasureChannel to start manually

  Revision 1.15  2004/10/22 07:35:18  sasakis
  Support for shared wh.

  Revision 1.14  2004/10/22 06:58:26  sasakis
  Added radio emission icon.

  Revision 1.13  2004/10/21 00:43:34  yosizaki
  add SHARED_WH switch.

  Revision 1.12  2004/10/06 05:11:35  sasakis
  Added graph display and such.

  Revision 1.11  2004/09/30 06:19:57  seiki_masashi
  Changed indent

  Revision 1.10  2004/09/30 06:16:07  seiki_masashi
  Changed the call back location of WM_StepDataSharing.

  Revision 1.9  2004/09/24 09:43:35  sasakis
  Some small bug fixes.

  Revision 1.8  2004/09/24 06:12:35  sasakis
  Added the channel manual selection feature.

  Revision 1.7  2004/09/22 09:39:33  sasakis
  Added the debug screen.

  Revision 1.6  2004/09/17 09:13:30  sasakis
  Modified to allow selection of the least crowded channel 
  by using MeasureChannel when it is a parent. (conditions are not yet determined.)

  Revision 1.5  2004/09/15 10:23:40  sasakis
  Added the radio receiving strength icon and made modification for allowing changes in data size. 

  Revision 1.4  2004/09/10 08:44:52  sasakis
  Added the guidelines compliance process, reviewed error related issues and such.

  Revision 1.3  2004/09/08 08:55:35  sasakis
  Made a small enhancement to the disconnection process.

  Revision 1.2  2004/09/08 01:52:52  sasakis
  Improved the error/disconnection related issues.

  Revision 1.1  2004/09/07 04:59:42  sasakis
  Registration of the initial version.

  $NoKeywords: $
 *---------------------------------------------------------------------------*/

#include <nitro.h>
#include <nitro/wm.h>
#include <string.h>

#include "main.h"
#include "font.h"
#include "print.h"
#include "key.h"
#include "graphics.h"
#include "wh.h"
#include "menu.h"

#if (defined __MWERKS__)
#pragma warn_resultnotused off
#endif

/* GGID used in this demo  */
#define WH_GGID           SDK_MAKEGGID_SYSTEM(0x13)
#define GRAPH_TOTAL_FRAME 60

typedef struct GraphData_ {
    u16 level;
    s16 data;
}GraphData;

static u16        sForcedChannel = 0;
static u16        sTgid          = 0;
static int        sSysMode       = 0;
static int        sSysModeStep   = 0;

static s32        sFrame         = 0;
static u8         sSendBuf[512] ATTRIBUTE_ALIGN(32);
static u8         sRecvBuf[512] ATTRIBUTE_ALIGN(32);
static BOOL       sRecvFlag[WM_NUM_MAX_CHILD + 1];

static WMKeySet   sKeySet;

static GraphData  sGraphData[1 + WM_NUM_MAX_CHILD][GRAPH_TOTAL_FRAME];
static PRScreen   sInfoScreen  ATTRIBUTE_ALIGN(32);
static PRScreen   sDebugScreen ATTRIBUTE_ALIGN(32);

static int        sBeaconCount  = 0;
static int        sNeedWait     = 0;
static BOOL       sGraphEnabled = 0;

static WMBssDesc  sBssDesc[3];
static Window     sRoleMenuWindow;
static Window     sSelectChannelWindow;
static Window     sSelectParentWindow;
static Window     sLobbyWindow;
static Window     sErrorWindow;
static Window     sBusyWindow;
static Window     sOptionWindow;
static Window     sWaitWindow;

extern const unsigned char  wlicon_image[];
extern const unsigned short wlicon_palette[];

static void TraceWH(const char *fmt, ...)
{
    va_list vlist;
    va_start(vlist, fmt);
    PR_VPrintString(&sDebugScreen, fmt, vlist);
    va_end(vlist);
}

static void printString(const char *fmt, ...)
{
    va_list vlist;
    va_start(vlist, fmt);
    PR_VPrintString(&sInfoScreen, fmt, vlist);
    va_end(vlist);
}

BOOL isDataReceived(int index)
{
    return sRecvFlag[index];
}

ShareData *getRecvData(int index)
{
    return (ShareData *)(&(sRecvBuf[index * sizeof(ShareData)]));
}

ShareData *getSendData(void)
{
    return (ShareData *)sSendBuf;
}

PRScreen *getInfoScreen(void)
{
    return &sInfoScreen;
}

void changeSysMode(int s)
{
    if(sSysMode == s){
        return;
    }

    OS_Printf("sysmode = %d\n", s);
    sSysMode = s;
    sSysModeStep = 0;
}

static void indicateCallback(void *arg)
{
    WMIndCallback *cb;
    cb = (WMIndCallback *)arg;
    if(cb->state == WM_STATECODE_BEACON_RECV){
        sBeaconCount = 2;
    }
}

static void scanCallback(WMBssDesc *bssdesc)
{
    char buf[ITEM_LENGTH_MAX];
    int i;
    
    for(i = 0; i < sSelectParentWindow.itemnum; ++i){
        if(memcmp(sBssDesc[i].bssid, bssdesc->bssid, 12) == 0){
            // The parent with the same bssid is excluded from the same entry.
            return;
        }
    }
    
    WH_PrintBssDesc(bssdesc);
    
    sBssDesc[sSelectParentWindow.itemnum] = *bssdesc;
    snprintf(buf,
             ITEM_LENGTH_MAX,
             "[%d]channel%d",
             sSelectParentWindow.itemnum + 1,
             bssdesc->channel);
    addItemToWindow(&sSelectParentWindow, buf);
    WH_PrintBssDesc(bssdesc);
}

static void forceSpinWait(void)
{
    static int waitcycle = 0;
    
    if(getKeyInfo()->cnt & PAD_BUTTON_L){
        waitcycle += 4000;
        OS_Printf("wait = %d\n", waitcycle);
        
    }else if(getKeyInfo()->cnt & PAD_BUTTON_R){
        waitcycle -= 4000;
        if(waitcycle < 0){
            waitcycle = 0;
        }
        OS_Printf("wait = %d\n", waitcycle);
    }
    
    OS_SpinWait((u32)waitcycle);
}

static void ModeSelectRole(void)
{
    static const char *menuitems[] = {
        "Start (Parent mode)",
        "Start (Child mode)",
        "Option",
        NULL
    };
    
    if(sSysModeStep == 0){
        sRoleMenuWindow.selected = 0;
        setupWindow(&sRoleMenuWindow,
                    16, 16,
                    WIN_FLAG_SELECTABLE,
                    24, 24, 16);
        if(sRoleMenuWindow.itemnum == 0){
            int i;
            for(i = 0; menuitems[i] != NULL; ++i){
                addItemToWindow(&sRoleMenuWindow, menuitems[i]);
            }
        }
        openWindow(&sRoleMenuWindow);
    }
    
    if(sRoleMenuWindow.state == WIN_STATE_CLOSED){
        if(sRoleMenuWindow.selected < 0){
            openWindow(&sRoleMenuWindow);
            return;
        }
            
        switch(sRoleMenuWindow.selected) {
        case 0:
            if ( sForcedChannel == 0 ) {
                // Obtains an appropriate channel from the radio wave usage rate and makes a connection.
                WH_StartMeasureChannel();
                
            } else {
                // Use the channel that was selected manually to start the connection.
                sTgid++;
                WH_ParentConnect( WH_CONNECTMODE_DS_PARENT, sTgid, sForcedChannel );
            }
            changeSysMode(SYSMODE_LOBBY);
            break;
            
        case 1:
            {
                // Searches for a parent.
                static const u8 ANY_PARENT[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
                enum { ALL_CHANNEL = 0 };
                
                initWindow(&sSelectParentWindow);
                setupWindow(&sSelectParentWindow,
                            16, 16,
                            WIN_FLAG_SELECTABLE,
                            8, 8, 16);
                WH_StartScan(scanCallback, ANY_PARENT, ALL_CHANNEL);
                changeSysMode(SYSMODE_SCAN_PARENT);
            }
            break;
            
        case 2:
            // Option operation
            changeSysMode(SYSMODE_OPTION);
            break;

        default:
            break;
        }
    }
}

static void ModeOption(void)
{
    if(sSysModeStep == 0){
        initWindow(&sOptionWindow);
        setupWindow(&sOptionWindow,
                    16, 16,
                    WIN_FLAG_SELECTABLE,
                    8, 8, 16);
        addItemToWindow(&sOptionWindow, "Select channel (parent)");
        
        if(sGraphEnabled){
            addItemToWindow(&sOptionWindow, "Disable power graph");
        }else{
            addItemToWindow(&sOptionWindow, "Enable power graph");
        }
        
        openWindow(&sOptionWindow);
        return;
    }

    if(sOptionWindow.state == WIN_STATE_CLOSED){
        if(sOptionWindow.selected < 0){
            changeSysMode(SYSMODE_SELECT_ROLE);
            return;
        }
        
        if(sOptionWindow.selected == 0){
            changeSysMode(SYSMODE_SELECT_CHANNEL);
            
        }else if(sOptionWindow.selected == 1){
            sGraphEnabled = sGraphEnabled ? FALSE : TRUE;
            changeSysMode(SYSMODE_SELECT_ROLE);
        }
    }
}

static void ModeLobby(void)
{   
    u16 bmap;
    int i;

    if(sSysModeStep == 0){
        initWindow(&sLobbyWindow);
        setupWindow(&sLobbyWindow,
                    16, 16,
                    WIN_FLAG_NONE,
                    8, 8, 16);
        
        for(i = 0; i < WH_CHILD_MAX; ++i){
            addItemToWindow(&sLobbyWindow, "");
        }
        
        addItemToWindow(&sLobbyWindow, "");
        addItemToWindow(&sLobbyWindow, "Push A to start");
        
        openWindow(&sLobbyWindow);
        return;
    }
    
    bmap = WH_GetBitmap();
    for(i = 0; i < WH_CHILD_MAX; ++i){
        if(bmap & (1 << i)){
            snprintf(sLobbyWindow.item[i],
                     ITEM_LENGTH_MAX,
                     "[%02d] - entry", i);
        }else{
            snprintf(sLobbyWindow.item[i],
                     ITEM_LENGTH_MAX,
                     "[%02d] - waiting", i);
        }
    }
    
    if(sLobbyWindow.state == WIN_STATE_CLOSED){
        getSendData()->command = SHARECMD_NONE;
        if(sLobbyWindow.selected < 0){
            WH_Finalize();
            changeSysMode(SYSMODE_SELECT_ROLE);
            return;
        }
        
        changeSysMode(SYSMODE_PARENT);
    }
}

static void ModeLobbyWait(void)
{
    if(sSysModeStep == 0){
        initWindow(&sWaitWindow);
        setupWindow(&sWaitWindow,
                    32, 56,
                    WIN_FLAG_NOCONTROL,
                    8, 8, 8);
        addItemToWindow(&sWaitWindow, "\\2Accepted.");
        addItemToWindow(&sWaitWindow, "\\2Waiting for parent...");
        openWindow(&sWaitWindow);
        return;
    }
    
    if(getRecvData(0)->command == SHARECMD_EXITLOBBY){
        closeWindow(&sWaitWindow);
        changeSysMode(SYSMODE_CHILD);
    }
}

static void ModeSelectChannel(void)
{
    if(sSysModeStep == 0){
        setupWindow(&sSelectChannelWindow,
                    16, 16,
                    WIN_FLAG_SELECTABLE,
                    16, 12, 16);
        
        if(sSelectChannelWindow.itemnum == 0){
            u16 pattern;
            int i, j;
            pattern = WH_GetAllowedChannel();
            addItemToWindow(&sSelectChannelWindow, "Auto select");
            for(i = 1, j = 1; i <= 14; ++i){
                if(pattern & (1 << (i - 1))){
                    char buf[ITEM_LENGTH_MAX];
                    snprintf(buf, ITEM_LENGTH_MAX,  "Channel %d", i);
                    ++j;
                    addItemToWindow(&sSelectChannelWindow, buf);
                }
            }
        }
        
        openWindow(&sSelectChannelWindow);
    }
    
    if(sSelectChannelWindow.state == WIN_STATE_CLOSED){
        if(sSelectChannelWindow.selected >= 0){
            sForcedChannel = (u16)sSelectChannelWindow.selected;
        }
        
        changeSysMode(SYSMODE_SELECT_ROLE);
    }
}

static void ModeSelectParent(void)
{
    if(sSysModeStep == 0){
        openWindow(&sSelectParentWindow);
    }
    
    if((sSelectParentWindow.state == WIN_STATE_CLOSED)){
        if (WH_GetSystemState() == WH_SYSSTATE_SCANNING){
            (void)WH_EndScan();
            return;
        }
        
        if (WH_GetSystemState() == WH_SYSSTATE_IDLE){
            if(sSelectParentWindow.selected < 0){
                WH_Finalize();
                changeSysMode(SYSMODE_SELECT_ROLE);
                return;
            }
            
            WH_ChildConnect(WH_CONNECTMODE_DS_CHILD,
                            &(sBssDesc[sSelectParentWindow.selected]));
            changeSysMode(SYSMODE_LOBBYWAIT);
        }
    }
}

static void ModeError(void)
{
    if(sSysModeStep == 0){
        initWindow(&sErrorWindow);
        setupWindow(&sErrorWindow, 16, 16, WIN_FLAG_NONE, 8, 8, 16);
        
        addItemToWindow(&sErrorWindow, "\\1Error has occured!");

        if(WH_GetLastError() == WM_ERRCODE_OVER_MAX_ENTRY){
            addItemToWindow(&sErrorWindow, "\\4Rejected\n");
        }
        
        if(WH_GetLastError() == WH_ERRCODE_DISCONNECTED){
            addItemToWindow(&sErrorWindow, "\\4Disconnected by parent\n");
        }
        
        if(WH_GetLastError() == WH_ERRCODE_PARENT_NOT_FOUND){
            addItemToWindow(&sErrorWindow, "\\4Parent not found\n");
        }
        
        if(WH_GetLastError() == WH_ERRCODE_LOST_PARENT){
            addItemToWindow(&sErrorWindow, "\\4Lost parent\n");
        }
        
        addItemToWindow(&sErrorWindow, "");
        addItemToWindow(&sErrorWindow, "\\fPush A to reset");

        closeAllWindow();
        openWindow(&sErrorWindow);
    }
    
    if(sErrorWindow.state == WIN_STATE_CLOSED){
        WH_Finalize();
        changeSysMode(SYSMODE_SELECT_ROLE);
    }
}

static void printShareData(void)
{
    s32 i;
    ShareData *sd;

    sd = getSendData();
    printString("\\2Send:     0x%04x 0x%04x\n", sd->key, sd->count & 0xffff);
    
    printString("\\4Receive:\n");
    for(i = 1; i < (WM_NUM_MAX_CHILD + 1); i++){
        if(sRecvFlag[i]){
            sd = getRecvData(i);
            printString("\\4Child%02d:  0x%04x 0x%04x\n",
                        i,
                        sd->key,
                        sd->count & 0xffff);
                        
        }else{
            printString("No child\n");
        }
    }
 }

static void ModeParent(void)
{
    printString("\n  \\fParent mode\n\n" );
    printShareData();
}

static void ModeChild(void)
{
    ShareData *sd;
    
    printString("\n  \\fChild mode\n\n");
    printShareData();
    
    sd = (ShareData *)getRecvData(0);
    printString("\\4Parent:   0x%04x 0x%04x\n",
                sd->key,
                sd->count & 0xffff);
}

static void VBlankIntr( void )
{
    // To have a stable communication, WM_StepDataSharing and WM_GetKeySet must be called
    // before the start of the MP communication of that frame. 
    // With SDK, the V count that prepares the default MP communication is set to
    // 240 for a child, and 260 for a parent. There will be least problems if it is set
    // immediately after starting V count.
    // In the future, the V count for starting MP communication will be able to set at the time of initialization.

    // The game that runs at 30 fps requires codes that regulate the frequency of calling StepDS
    // to once every two frames.
    // In that case, when WM_ERRCODE_NO_DATASET was returned (if the process failure occurred),
    // set to wait for 1 frame instead of 2 frames.
    // Otherwise, when there is one frame difference between the parent and child, it cannot be corrected.
    
    updateKeys();
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}

static void initAllocSystem(void)
{
    void*        tempLo;
    OSHeapHandle hh;

    tempLo = OS_InitAlloc(OS_ARENA_MAIN,
                          OS_GetMainArenaLo(),
                          OS_GetMainArenaHi(),
                          1);
    OS_SetArenaLo(OS_ARENA_MAIN, tempLo);
    hh = OS_CreateHeap(OS_ARENA_MAIN ,
                       OS_GetMainArenaLo(),
                       OS_GetMainArenaHi());
    if(hh < 0){
        OS_Panic("ARM9: Fail to create heap...\n");
    }
    hh = OS_SetCurrentHeap(OS_ARENA_MAIN, hh);
}

static void drawPowerGraph(void)
{
    static const GXRgb linecolor[4] = {
        GX_RGB(15,  0,  0),  // dark red (dead)
        GX_RGB(31, 31,  0),  // yellow
        GX_RGB( 0, 31,  0),  // green
        GX_RGB(20, 31, 20),  // light green
    };
    
    int midx, ringidx;
    
    ringidx = (sFrame % GRAPH_TOTAL_FRAME);
    
    for(midx = 0; midx < WM_NUM_MAX_CHILD + 1; ++midx){
        sGraphData[midx][ringidx].data  = (s16)getRecvData(midx)->data;
        sGraphData[midx][ringidx].level = (u16)getRecvData(midx)->level;
    }
    
    G3_PolygonAttr(GX_LIGHTMASK_NONE,
                   GX_POLYGONMODE_MODULATE,
                   GX_CULL_NONE,
                   0,
                   31,
                   GX_POLYGON_ATTR_MISC_DISP_1DOT);
    G3_TexImageParam(GX_TEXFMT_NONE,
                     GX_TEXGEN_NONE,
                     GX_TEXSIZE_S16,
                     GX_TEXSIZE_T16,
                     GX_TEXREPEAT_NONE,
                     GX_TEXFLIP_NONE,
                     GX_TEXPLTTCOLOR0_USE,
                     0);
    
    G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
    G3_PushMtx();
    G3_Identity();
    G3_Translate(0, 0, -FX16_ONE * 4);
    
    G3_Begin(GX_BEGIN_TRIANGLES);
    
    for(midx = 0; midx < WM_NUM_MAX_CHILD + 1; ++midx){
        int basey, ys, ye, gi, x, level;
        basey = ((WM_NUM_MAX_CHILD / 2 - midx) * 9 + 6)* FX16_ONE / 64;
                
        for(gi = 0; gi < GRAPH_TOTAL_FRAME; ++gi){
            int ri;
            ri = (ringidx - gi);
            if(ri < 0){
                ri += GRAPH_TOTAL_FRAME;
            }
                    
            ys = sGraphData[midx][ri].data;
            level = sGraphData[midx][ri].level;
                    
            ++ri;
            if(ri >= GRAPH_TOTAL_FRAME){
                ri -= GRAPH_TOTAL_FRAME;
            }
                    
            ye = sGraphData[midx][ri].data;
                    
            x = -(gi - GRAPH_TOTAL_FRAME / 2) * 3;
            x *= FX16_ONE / 64;
            ys = ys * FX16_ONE / 64 + basey;
            ye = ye * FX16_ONE / 64 + basey;
                    
            G3_Color(linecolor[level]);
                    
            G3_Vtx((fx16)x, (fx16)ys, 0);
            G3_Vtx((fx16)(x + FX16_ONE / 64 / 2), (fx16)(ys + FX16_ONE / 64), 0);
            G3_Vtx((fx16)(x + 3 * FX16_ONE / 64), (fx16)ye, 0);
        }
    }
            
    G3_End();
    G3_PopMtx(1);
}

static void drawPowerIcon(void)
{
    // GUIDELINE
    // Display the radio receiving strength icon.
    setupPseudo2DCamera();
    
    G3_PolygonAttr(GX_LIGHTMASK_NONE,
                   GX_POLYGONMODE_DECAL,
                   GX_CULL_NONE,
                   0,
                   31,
                   0);
    G3_PushMtx();
    G3_MtxMode(GX_MTXMODE_TEXTURE);
    G3_Identity();
    
    G3_TexImageParam(GX_TEXFMT_PLTT16,
                     GX_TEXGEN_TEXCOORD,
                     GX_TEXSIZE_S16,
                     GX_TEXSIZE_T16,
                     GX_TEXREPEAT_NONE,
                     GX_TEXFLIP_NONE,
                     GX_TEXPLTTCOLOR0_USE,
                     0x2000 + WM_GetLinkLevel() * 16 * 16 / 2);
    G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
    G3_MtxMode(GX_MTXMODE_POSITION_VECTOR);
    drawPseudo2DTexQuad(224, 160, 16, 16, 16, 16);
}

static void drawRadioIcon(void)
{
    // GUIDELINE
    // Display the radio wave strength icon.
    int i;
    G3_TexPlttBase(0x2000, GX_TEXFMT_PLTT16);
    G3_TexImageParam(GX_TEXFMT_PLTT16,
                     GX_TEXGEN_TEXCOORD,
                     GX_TEXSIZE_S16,
                     GX_TEXSIZE_T16,
                     GX_TEXREPEAT_NONE,
                     GX_TEXFLIP_NONE,
                     GX_TEXPLTTCOLOR0_USE,
                     0x2000 + 4 * 16 * 16 / 2);
        
    for(i = 0; i < 2; ++i){
        drawPseudo2DTexQuad(16,
                            12 + i * 24 + ((i == sRoleMenuWindow.selected) ? (sFrame / 15 & 1) : 0),
                            16, 16,
                            16, 16);
    }
}

static void updateShareData(void)
{
    if(WH_GetSystemState() == WH_SYSSTATE_DATASHARING){
        if(WH_StepDS(sSendBuf)){
            u16 i;
            for(i = 0; i < WM_NUM_MAX_CHILD + 1; ++i){
                u8 *adr;
                ShareData *sd;
                
                adr = (u8 *)WH_GetSharedDataAdr(i);
                sd = (ShareData *)&(sRecvBuf[i * sizeof(ShareData)]);
                
                if(adr != NULL){
                    MI_CpuCopy8(adr, sd, sizeof(ShareData));
                    sRecvFlag[i] = TRUE;
                    
                }else{
                    sd->level = 0;
                    sd->data = 0;
                    sRecvFlag[i] = FALSE;
                }
            }
            
            sNeedWait = FALSE;
            
        }else{
            u16 i;
            for(i = 0; i < WM_NUM_MAX_CHILD + 1; ++i){
                sRecvFlag[i] = FALSE;
            }
            
            sNeedWait = TRUE;
        }
        
    }else{
        u16 i;
        for(i = 0; i < WM_NUM_MAX_CHILD + 1; ++i){
            sRecvFlag[i] = FALSE;
        }
        
        sNeedWait = FALSE;
    }
}

static void packSendData(void)
{
    ShareData *senddata;
    
    if(sNeedWait){
        return;
    }
    
    senddata = getSendData();
    senddata->command =
        (sSysMode == SYSMODE_LOBBY) ? SHARECMD_NONE : SHARECMD_EXITLOBBY;
    senddata->level = (u16)WM_GetLinkLevel();
    senddata->data = 0;
    senddata->key = getKeyInfo()->cnt;
    senddata->count = sFrame & 0xffff;
    
    if(sBeaconCount != 0){
        senddata->data += sBeaconCount * senddata->level;
        
        if(sBeaconCount > 0){
            sBeaconCount = -sBeaconCount + 1;
        }else{
            sBeaconCount = -sBeaconCount - 1;
        }
    }
}

static void mainLoop(void)
{
    for ( sFrame = 0; TRUE; sFrame++ ) 
    {
        int whstate;
        int prevmode;

        whstate = WH_GetSystemState();
        prevmode = sSysMode;

        switch(whstate){
        case WH_SYSSTATE_ERROR:
            // WH state has the priority if an error occurred.
            changeSysMode(SYSMODE_ERROR);
            break;

        case WH_SYSSTATE_MEASURECHANNEL:
            {
                u16 channel = WH_GetMeasureChannel();
                sTgid++;
                WH_ParentConnect(WH_CONNECTMODE_DS_PARENT, sTgid, channel);
            }
            break;
            
        default:
            break;
        }
        
        PR_ClearScreen(&sInfoScreen);

        // Load test.
        forceSpinWait();
        
        switch(sSysMode){
        case SYSMODE_SELECT_ROLE:
            // Role (parent or child) selection screen
            ModeSelectRole();
            break;

        case SYSMODE_SELECT_CHANNEL:
            // Channel selection screen
            ModeSelectChannel();
            break;

        case SYSMODE_LOBBY:
            // Lobby screen
            ModeLobby();
            break;

        case SYSMODE_LOBBYWAIT:
            // Lobby standby screen
            ModeLobbyWait();
            break;
            
        case SYSMODE_OPTION:
            // Option screen
            ModeOption();
            break;
            
        case SYSMODE_SCAN_PARENT:
        case SYSMODE_SELECT_PARENT:
            // Parent selection screen
            ModeSelectParent();
            break;
            
        case SYSMODE_ERROR:
            // Error report screen
            ModeError();
            break;
            
        case SYSMODE_PARENT:
            // Parent mode screen
            ModeParent();
            break;
            
        case SYSMODE_CHILD:
            // Child mode screen
            ModeChild();
            break;
            
        default:
            break;
        }

        if(
           (whstate == WH_SYSSTATE_BUSY)
           || ((whstate == WH_SYSSTATE_SCANNING)
               && (sSelectParentWindow.itemnum == 0))
           ){
            sBusyWindow.state = WIN_STATE_OPENED;
            
        }else{
            sBusyWindow.state = WIN_STATE_CLOSED;
        }
        
        updateAllWindow();
        drawAllWindow();
        
        drawPowerIcon();
        
        if((sSysMode == SYSMODE_PARENT)
           || (sSysMode == SYSMODE_CHILD)){
            if(sGraphEnabled){
                drawPowerGraph();
            }
        }

        if((sSysMode == SYSMODE_SELECT_ROLE)
           && (sRoleMenuWindow.state == WIN_STATE_OPENED)){
            drawRadioIcon();
        }
        
        G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W);
        
//         if(!sNeedWait
//            && ((sSysMode == SYSMODE_PARENT)
//                || (sSysMode == SYSMODE_CHILD))){
//             updateCharacter();
//         }

        DC_FlushRange(sInfoScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
        GX_LoadBG2Scr(sInfoScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
        
        DC_FlushRange(sDebugScreen.screen, sizeof(u16) * PR_SCREEN_SIZE);
        GXS_LoadBG2Scr(sDebugScreen.screen, 0, sizeof(u16) * PR_SCREEN_SIZE);
        
        SVC_WaitVBlankIntr();
        
        updateShareData();
        packSendData();
        
        OS_PrintServer();

        if(prevmode == sSysMode){
            ++sSysModeStep;
        }
    }
}

void NitroMain(void)
{
    OS_InitPrintServer();
    OS_Init();
    FX_Init();
    PXI_Init();
    
    initGraphics();
    
    GX_SetBankForBG(GX_VRAM_BG_256_AB);
    GX_SetBankForBGExtPltt(GX_VRAM_BGEXTPLTT_01_F);
    G2_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
                         GX_BG_COLORMODE_16,
                         GX_BG_SCRBASE_0x0000,  
                         GX_BG_CHARBASE_0x04000);
    
    GX_BeginLoadTex();
    GX_LoadTex(wlicon_image,
               0x2000,
               16 * 16 * 5);
    GX_EndLoadTex();
  
    GX_BeginLoadTexPltt();
    GX_LoadTexPltt(wlicon_palette,
                   0x2000,
                   32);
    GX_EndLoadTexPltt();

    GX_LoadBG2Char(d_CharData,   0, sizeof(d_CharData));
    GX_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
    
    GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);
    GX_SetBankForSubBGExtPltt(GX_VRAM_SUB_BGEXTPLTT_0123_H);
    G2S_SetBG2ControlText(GX_BG_SCRSIZE_TEXT_256x256,
                          GX_BG_COLORMODE_16,
                          GX_BG_SCRBASE_0x0000,  
                          GX_BG_CHARBASE_0x00000);
    
    GXS_SetGraphicsMode(GX_BGMODE_0);
    GXS_SetVisiblePlane(GX_PLANEMASK_BG2);
    G2S_SetBG2Priority(0);
    G2S_BG2Mosaic(FALSE);
    
    GXS_LoadBG2Char(d_CharData,   0, sizeof(d_CharData));
    GXS_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
    
    initAllocSystem();
    
    OS_SetIrqFunction( OS_IE_V_BLANK , VBlankIntr );
    OS_EnableIrqMask( OS_IE_V_BLANK );
    GX_VBlankIntr( TRUE );
    OS_EnableIrq();
    OS_EnableInterrupts();

    sInfoScreen.scroll  = FALSE;
    sDebugScreen.scroll = TRUE;
    
    // Wireless initialization
    WH_Initialize();
    WM_SetIndCallback(indicateCallback);
    
    // WH initial setting
    WH_SetGgid(WH_GGID);
    WH_SetDebugOutput(TraceWH);
    
    GX_DispOn();
    GXS_DispOn();

    initWindow(&sRoleMenuWindow);
    initWindow(&sSelectChannelWindow);
    initWindow(&sSelectParentWindow);
    initWindow(&sLobbyWindow);
    initWindow(&sErrorWindow);
    initWindow(&sOptionWindow);
    initWindow(&sWaitWindow);
    initWindow(&sBusyWindow);
    
    setupWindow(&sBusyWindow, 64, 80, WIN_FLAG_NOCONTROL, 8, 8, 16);
    addItemToWindow(&sBusyWindow, "\\2Working...");
    
    // Countermeasure for pressing A button at IPL
    updateKeys();
    
    // initCharacter();
    changeSysMode(SYSMODE_SELECT_ROLE);
    mainLoop();
}

// int getWindowSelected(Window *win)
// {
//     // FIXME
//     ;
// }
