/*---------------------------------------------------------------------------*
  Project:  NitroSDK - MB - demos - multiboot-Model
  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.22  2004/11/12 09:01:20  yosizaki
  change mb_parent to /wireless_shared/mbp

  Revision 1.21  2004/11/10 01:12:28  takano_makoto
  Only 14 children were displayed. Fixed.

  Revision 1.20  2004/11/02 18:08:07  takano_makoto
  fix comments.

  Revision 1.19  2004/10/28 01:31:09  takano_makoto
  Moved wmhigh to wh under wireless_shared.

  Revision 1.18  2004/10/27 02:06:41  takano_makoto
  Revised to increment tgid after a restart.

  Revision 1.17  2004/10/25 06:29:56  takano_makoto
  Added a setting for maximum connected children.

  Revision 1.16  2004/10/21 00:42:42  yosizaki
  add SHARED_WH switch.

  Revision 1.15  2004/10/18 11:44:06  yosizaki
  change to use MB_StartParentEx/MB_EndEx (switch)

  Revision 1.14  2004/10/08 01:27:10  takano_makoto
  Fixed offset in the displayed child number.

  Revision 1.13  2004/10/05 09:45:29  terui
  Standardized MAC address with u8-type array.

  Revision 1.12  2004/10/05 07:49:36  takano_makoto
  Revised so that child Accept processes in mb_parent.c callback.

  Revision 1.11  2004/10/05 03:58:49  takano_makoto
  Make small revisions to comments.

  Revision 1.10  2004/10/05 02:15:05  takano_makoto
  Changed to wait in entry state and start simultaneous download on all children.

  Revision 1.9  2004/09/30 08:28:26  yosizaki
  add SDK_MAKEGGID_SYSTEM macro.

  Revision 1.8  2004/09/27 08:35:49  takano_makoto
  Fixed the problem where channel number changed when restarting parent device.

  Revision 1.7  2004/09/21 07:26:06  takano_makoto
  Comment addition

  Revision 1.4  2004/09/17 10:36:11  takano_makoto
  Changed so BeconPeriod is adjusted through the standard function srand

  Revision 1.3  2004/09/17 06:11:59  takano_makoto
  Changed so the user information is fetched through MBP_GetChildInfo()

  Revision 1.2  2004/09/17 02:20:23  sato_masaki
  Added reply comments for TODO.

  Revision 1.1  2004/09/16 14:06:41  takano_makoto
  Initial Upload

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


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

#include "mbp.h"
#include "common.h"
#include "disp.h"
#include "gmain.h"
#include "wh.h"

/*
 * Application sample for connecting after multi-boot.
 *
 * The MB library sample uses the multi-boot function, so
 * multiple development machines are needed with the same communication environment (wireless or cable).
 * The mb_child.bin program in the $NitroSDK/bin/ARM9-TS/Release/ directory
 * is a sample that provides functionality equivalent to the multi-boot child
 * in the final actual machine, so
 * this binary should be read in the other machines in the same manner as the sample program
 * and they should be executed together.
 *
 */

/******************************************************************************/

static void GetChannelMain( void );
static BOOL ConnectMain( u16 tgid );
static void PrintChildState( void );
static BOOL JudgeConnectableChild( WMStartParentCallback* cb );


//============================================================================
//  Constant Definitions
//============================================================================

/* GGID used in this demo */
#define WH_GGID                 SDK_MAKEGGID_SYSTEM(0x21)


/* Program information the demo downloads  */
const MBGameRegistry mbGameList =
{
    "/child.srl",               // Child binary code
    (u16*)L"DataShareDemo",     // Game name
    (u16*)L"DataSharing demo",   // Game internal description
    "/data/icon.char",          // Icon character data
    "/data/icon.plt",           // Icon palette data
    WH_GGID,                    // GGID
    MBP_CHILD_MAX + 1,          // Maximum number of players, including the parent.
};



//============================================================================
//   Variable Definition
//============================================================================

static s32      gFrame;                 // Frame counter

//-----------------------
// For saving the communication pathway
//-----------------------
static u16 sChannel = 0;
static const MBPChildInfo* sChildInfo[ MBP_CHILD_MAX ];


//============================================================================
//   Variable Definition
//============================================================================

/*---------------------------------------------------------------------------*
  Name:         NitroMain

  Description:  Main routine

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
void NitroMain( void )
{
    u16 tgid = 0;

    // Screen and OS initialization
    CommonInit();
    // Screen initialization
    DispInit();
    // Heap initialization
    InitAllocateSystem();

    // Set information in WH.
    WH_SetGgid(WH_GGID);

    // Enables interrupt
    (void)OS_EnableIrq();
    (void)OS_EnableInterrupts();

    DispOn();

    while ( TRUE )
    {
        SVC_WaitVBlankIntr();

        // Search processing for channels with little traffic
        GetChannelMain();

        /*
         * tgid sets a value that is generally different from the previous value each time the parent starts up.
         * If the same tgid is not used, however, care is required as a multi-boot child will not
         * be able to reconnect without rescanning.
         * If another scan is run before reconnecting, there is no need to
         * save tgid.
         */
        // Multi-boot distribution processing
        if ( ConnectMain( ++tgid ) )
        {
            // Success in starting the multi-boot child
            break;
        }
    }

    //--------------
    // After multi-booting, the child is reset and communication is temporarily stopped.
    // With the current state, the parent also needs to end communication after MB_End().
    // Establish communication from the beginning with the parent and children all in a state of complete disconnect.
    //
    // The aid for the child will be shuffled at this time, so save the combination of
    // the MAC address and aid before the multi-boot, and
    // tie together with a new aid when reconnecting if necessary.
    //--------------


    // Sets the buffer for data sharing communication
    GInitDataShare();

#if !defined(MPB_USING_MB_EX)
    (void)WH_Initialize();
#endif

    // Sets the function to determine the connected children
    WH_SetJudgeAcceptFunc( JudgeConnectableChild );

    /* Main routine  */
    for ( gFrame = 0 ; TRUE ; gFrame++ )
    {
        SVC_WaitVBlankIntr();

        ReadKey();

        BgClear();

        switch ( WH_GetSystemState() )
        {
        case WH_SYSSTATE_IDLE :
            /* ----------------
             * In the case of a child reconnecting to the same parent without scanning,
             * you must align the child, to the tgid and channel.
             * In this demo, parent and child use the same channel as during multiboot, and the multiboot's tgid+1.
             * Therefore, it is possible to connect without rescanning.
             *
             * When specifying the MAC address and rescanning, there is no need to use the same tgid or channel.
             *
             * ---------------- */
            (void)WH_ParentConnect( WH_CONNECTMODE_DS_PARENT, (u16)(tgid + 1), sChannel );
            break;

        case WH_SYSSTATE_CONNECTED:
        case WH_SYSSTATE_KEYSHARING:
        case WH_SYSSTATE_DATASHARING:
            {
                BgPutString( 8 , 1 , 0x2 , "Parent mode" );
                GStepDataShare( gFrame );
                GMain();
            }
            break;
        }
    }
}

/*---------------------------------------------------------------------------*
  Name:         GetChannelMain

  Description:  Checks the signal use rate for the channel being used.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void GetChannelMain( void )
{

    /*-----------------------------------------------*
     * After thoroughly examining the signal use rate of the channels
     * the channel with the lowest use is selected.
     * To execute WM_MeasureChannel(), an IDLE state is required.
     * In a multi-boot state, it is not possible to stop in an IDLE state, so this function cannot be executed.
     * In this case, call WM_Initialize, check the signal use rate, then end with WM_End
     * and execute MB_Init again.
     *-----------------------------------------------*/
    (void)WH_Initialize();

    while ( TRUE )
    {
        ReadKey();
        BgClear();
        BgSetMessage( PLTT_YELLOW, " Search Channel " );

        switch ( WH_GetSystemState() )
        {
        //-----------------------------------------
        // Ends initialization
        case WH_SYSSTATE_IDLE:
            BgSetMessage( PLTT_WHITE, " Push A Button to start   ");
            if ( IS_PAD_TRIGGER( PAD_BUTTON_A ) )
            {
                BgSetMessage( PLTT_YELLOW, "Check Traffic ratio       ");
                (void)WH_StartMeasureChannel();
            }
            break;
        //-----------------------------------------
        // Ends the channel search
        case WH_SYSSTATE_MEASURECHANNEL:
            {
                sChannel = WH_GetMeasureChannel();
#if !defined(MPB_USING_MB_EX)
                (void)WH_End();
#else
                /* Move to multiboot process while maintaining IDLE state.*/
                return;
#endif
            }
            break;
        //-----------------------------------------
        // Ends WM
        case WH_SYSSTATE_STOP:
            /* If WM_End is complete, moves to multi-boot  */
            return;
        //-----------------------------------------
        // Currently busy
        case WH_SYSSTATE_BUSY:
            break;
        //-----------------------------------------
        // Error occurred
        case WH_SYSSTATE_ERROR:
            (void)WH_Reset();
            break;
        //-----------------------------------------
        default:
            OS_Panic("Illegal State\n");
        }
        SVC_WaitVBlankIntr();       // Waiting for the V-blank interrupt to end
    }
}


/*---------------------------------------------------------------------------*
  Name:         ConnectMain

  Description:  Connects with multi-boot.

  Arguments:    tgid        Specifies tgid when starting as a parent.

  Returns:      Returns TRUE if transfer to the child is successful, and
                FALSE if it fails or is cancelled.
 *---------------------------------------------------------------------------*/
static BOOL ConnectMain( u16 tgid )
{
    MBP_Init( mbGameList.ggid, tgid );

    while ( TRUE )
    {
        ReadKey();

        BgClear();

        BgSetMessage( PLTT_YELLOW, " MB Parent " );

        switch ( MBP_GetState() )
        {
        //-----------------------------------------
        // Idle state
        case MBP_STATE_IDLE :
            {
                MBP_Start( &mbGameList, sChannel );
            }
            break;

        //-----------------------------------------
        // receiving entry from child
        case MBP_STATE_ENTRY :
            {
                BgSetMessage( PLTT_WHITE, " Now Accepting            ");

                if ( IS_PAD_TRIGGER( PAD_BUTTON_B ) )
                {
                // cancel multiboot with B button
                    MBP_Cancel();
                    break;
                }

                // Enable start if there is at least one entered child.
                if ( MBP_GetChildBmp( MBP_BMPTYPE_ENTRY ) )
                {
                    BgSetMessage( PLTT_WHITE, " Push START Button to start   ");

                    if ( IS_PAD_TRIGGER( PAD_BUTTON_START ) )
                    {
                        // start download
                        MBP_StartDownloadAll();
                    }
                }
            }
            break;

        //-----------------------------------------
        // program distribution process
        case MBP_STATE_DATASENDING :
            {

                // Start is possible if all have finished downloading.
                if ( MBP_IsBootableAll() )
                {
                    // start boot
                    MBP_StartRebootAll();
                }
            }
            break;

        //-----------------------------------------
        // reboot processing
        case MBP_STATE_REBOOTING:
            {
                BgSetMessage( PLTT_WHITE, " Rebooting now                ");
            }
            break;

        //-----------------------------------------
        // Reconnection processing
        case MBP_STATE_COMPLETE :
            {
                // After all members have successfully connected and the multi-boot processing is complete,
                // restarts the wireless as an ordinary parent.
                BgSetMessage( PLTT_WHITE, " Reconnecting now             ");

                SVC_WaitVBlankIntr();
                return TRUE;
            }
            break;

        //-----------------------------------------
        // Error occurred
        case MBP_STATE_ERROR:
            {
            // Cancels communication
                MBP_Cancel();
            }
            break;

        //-----------------------------------------
        // Communication being cancelled
        case MBP_STATE_CANCEL:
            // None
            break;

        //-----------------------------------------
        // Communication ended abnormally
        case MBP_STATE_STOP:
            SVC_WaitVBlankIntr();
            return FALSE;
        }

        // Displays the child state
        PrintChildState();

        SVC_WaitVBlankIntr();       // Waiting for the V-blank interrupt to end
    }
}


/*---------------------------------------------------------------------------*
  Name:         PrintChildState

  Description:  Displays the child information on the screen

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void PrintChildState( void )
{
    static const char* STATE_NAME[] =
    {
        "NONE       ",
        "CONNECTING ",
        "REQUEST    ",
        "ENTRY      ",
        "DOWNLOADING",
        "BOOTABLE   ",
        "BOOTING    ",
    };
    enum
    {
        STATE_DISP_X = 2,
        INFO_DISP_X  = 15,
        BASE_DISP_Y  = 2
    };

    u16 i;

    /* Displays the child list */
    for ( i = 1; i <= MBP_CHILD_MAX; i++ )
    {
        const MBPChildInfo* childInfo;
        MBPChildState childState = MBP_GetChildState( i );
        const u8* macAddr;

        SDK_ASSERT( childState < MBP_CHILDSTATE_NUM );

        // State display
        BgPutString( STATE_DISP_X, i + BASE_DISP_Y, PLTT_WHITE, STATE_NAME[ childState ] );

        // User information display
        childInfo = MBP_GetChildInfo( i );
        macAddr = MBP_GetChildMacAddress( i );

        if ( macAddr != NULL )
        {
            BgPrintStr( INFO_DISP_X, i + BASE_DISP_Y, PLTT_WHITE,
                        "%02x%02x%02x%02x%02x%02x",
                        macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5] );
        }
    }
}


/*---------------------------------------------------------------------------*
  Name:         JudgeConnectableChild

  Description:  Function to determine whether the child can connect during a reconnect

  Arguments:    cb      Information for a child connecting

  Returns:      TRUE if the connection is accepted.
                FALSE if it is not accepted.
 *---------------------------------------------------------------------------*/
static BOOL JudgeConnectableChild( WMStartParentCallback* cb )
{
    u16 playerNo;

    /*  Searches for aid from the MAC address during cb -> aid child multi-boot  */
    playerNo = MBP_GetPlayerNo( cb->macAddress );

    OS_TPrintf( "MB child(%d) -> DS child(%d)\n", playerNo, cb->aid );

    if ( playerNo == 0 )
    {
        return FALSE;
    }

    sChildInfo[ playerNo ] = MBP_GetChildInfo( playerNo );
    return TRUE;
}
