/*---------------------------------------------------------------------------*
  Project:  NITRO-System - build - demos - mcs - basic - nitro
  File:     main.c

  Copyright 2005 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.5  2005/02/25 02:30:58  kitamurs
  Warning measures

  Revision 1.4  01/28/2005 04:52:07  kitamurs
  Changed things so picture is displayed at time of FINALROM build too.

  Revision 1.3  01/20/2005 07:35:45  kitamurs
  Cleaned up the code

  Revision 1.2  01/20/2005 05:10:12  kitamurs
  Added comment

  Revision 1.1  01/20/2005 04:44:48  kitamurs
  Initial Version

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

// ============================================================================
//  Explanation of the demo:
//      The mcs library is used to read and write data with the PC.
//      First, a picture is displayed to the BG screen.
//      When the PC program basic.exe is run, the picture moves horizontally.
//      The next time basic.exe is run, the picture moves diagonally.
//
//      Pressing the A Button quits the program.
//
// ============================================================================

#include "sdk_init.h"
#include "nns_util.h"

#include <nnsys/mcs.h>
#include <nnsys/misc.h>

// The channel values used to distinguish from Windows applications
static const u16 MCS_CHANNEL0 = 0;
static const u16 MCS_CHANNEL1 = 1;

// The structure for positional information
typedef struct Vec2 Vec2;
struct Vec2
{
    s32     x;
    s32     y;
};

/*---------------------------------------------------------------------------*
  Name:         VBlankIntr

  Description:  The V blank interrupt handler.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
VBlankIntr()
{
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);

    NNS_McsVBlankInterrupt();
}

/*---------------------------------------------------------------------------*
  Name:         CartIntrFunc

  Description:  The cartridge interrupt handler.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
CartIntrFunc()
{
    OS_SetIrqCheckFlag(OS_IE_CARTRIDGE);

    NNS_McsCartridgeInterrupt();
}

/*---------------------------------------------------------------------------*
  Name:         InitInterrupt

  Description:  Performs the initialization related to interrupts.

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
InitInterrupt()
{
    (void)OS_DisableIrq();

    // Enable V blank interrupts, and set so NNS_McsVBlankInterrupt()
    // gets called from inside VBlank interrupt.
    (void)OS_EnableIrqMask(OS_IE_V_BLANK);
    OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
    (void)GX_VBlankIntr(TRUE);

	// Enable cartridge interrupts, and set so NNS_McsCartridgeInterrupt()
    // gets called from inside cartridge interrupt
    OS_SetIrqFunction(OS_IE_CARTRIDGE, CartIntrFunc);
	(void)OS_EnableIrqMask(OS_IE_CARTRIDGE);

    (void)OS_EnableIrq();
}

/*---------------------------------------------------------------------------*
  Name:         DataRecvCallback

  Description:  The callback function that gets called when data  is received from the PC.

                Do not receive data inside the callback function to register.
                Also, since there are times when interrupts are prohibited,
                do not perform any interrupt wait loops.

  Arguments:    recv:       Pointer to the buffer where some or all of the
                            received data is stored.
                recvSize:   The amount of data stored in the buffer
                            indicated by recv.
                userData:   The value specified by the userData argument
                            of NNS_McsRegisterRecvCallback().
                offset:     If all of the received data is stored in
                            the buffer indicated by recv, this is 0.
                            If part of the received data is being stored,
                            0 is used as the standard offset position for all received data. 
                totalSize:  The total size of the received data.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
DataRecvCallback(
    const void* pRecv,
    u32         recvSize,
    u32         userData,
    u32         offset,
    u32         /* totalSize*/
)
{
    Vec2 *const pPos = (Vec2*)userData;

	// Check the reception buffer
	if (pRecv != NULL && recvSize == sizeof(pPos->y) && offset == 0)
	{
        // Update the y position with the received value
        const s32* pRecvPosY = pRecv;
        pPos->y = *pRecvPosY;
    }
}

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

  Description:  A sample program that reads BG data from an archive and
                displays it on the screen.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
static void
MainLoop(Vec2* pPos)
{
    while (TRUE)
    {
        u32 nLength;
        s32 posX;

        NNS_McsPollingIdle();

        // Get the size of data that can be received
		nLength = NNS_McsGetStreamReadableSize(MCS_CHANNEL0);
        if (nLength > 0 && sizeof(posX) == nLength)
        {
            u32 readSize;

            // Read the data
            if ( NNS_McsReadStream(MCS_CHANNEL0, &posX, sizeof(posX), &readSize)
              && readSize == sizeof(posX)
            )
            {
                pPos->x = posX;
            }
        }

        // If outside a certain range, convey to PC as inverse of  incremental value
        if (pPos->x < 0 || 64 < pPos->x)
        {
            s32 writeData = pPos->x < 0 ? 1: -1;
            // Write the data
            if (! NNS_McsWriteStream(MCS_CHANNEL0, &writeData, sizeof(writeData)))
            {
                break;
            }
        }

        // If outside a certain range, convey to PC as inverse of  incremental value
        if (pPos->y < 0 || 64 < pPos->y)
        {
            s32 writeData = pPos->y < 0 ? 1: -1;
            // Write the data
            if (! NNS_McsWriteStream(MCS_CHANNEL1, &writeData, sizeof(writeData)))
            {
                break;
            }
        }

        SVC_WaitVBlankIntr();

        G2_SetBG0Offset(pPos->x, pPos->y);

        ReadGamePad();
        if (IS_TRIGGER(PAD_BUTTON_A))
        {
            break;
        }
    }
}

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

  Description:  A sample program that reads BG data from an archive and
                displays it on the screen.

  Arguments:    None.

  Returns:      None.
   ------------------------------------------------------------------------- */
void
NitroMain(void)
{
    NNSMcsDeviceCaps deviceCaps;
    NNSMcsRecvCBInfo recvCBInfo;
    Vec2* pPos;

    InitSystem();

    // Initialize mcs
    NNS_McsInit();

    InitInterrupt();
    InitMemory();
    InitVRAM();

    InitDisplay();
    LoadPicture();

    pPos = NNS_FndAllocFromExpHeapEx(gAppHeap, sizeof(Vec2), 4);  // Secure memory for data

    // Initialize data
    pPos->x = 0;
    pPos->y = 0;

    G2_SetBG0Offset(pPos->x, pPos->y);

    // Open the device
    if (NNS_McsGetMaxCaps() > 0 && NNS_McsOpen(&deviceCaps))
    {
        void* printBuffer = NNS_FndAllocFromExpHeap(gAppHeap, 1024);        // Secure buffer for printing
        void* recvBuf = NNS_FndAllocFromExpHeapEx(gAppHeap, 1024, 4);       // Secure buffer for receiving data

        NNS_NULL_ASSERT(printBuffer);
        NNS_NULL_ASSERT(recvBuf);
        NNS_NULL_ASSERT(pPos);

        // Output with OS_Printf
        OS_Printf("device open\n");

        // Initialize mcs character string output
        NNS_McsInitPrint(printBuffer, NNS_FndGetSizeForMBlockExpHeap(printBuffer));

        // Output with NNS_McsPrintf
        // Display on console if connected to mcs server at this time.
        (void)NNS_McsPrintf("device ID %08X\n", deviceCaps.deviceID);

        // Register buffer for reading of data
        NNS_McsRegisterStreamRecvBuffer(MCS_CHANNEL0, recvBuf, NNS_FndGetSizeForMBlockExpHeap(recvBuf));

        // Register the reception callback function
        NNS_McsRegisterRecvCallback(&recvCBInfo, MCS_CHANNEL1, DataRecvCallback, (u32)pPos);

        MainLoop(pPos);

        NNS_McsUnregisterRecvResource(MCS_CHANNEL1);
        NNS_McsUnregisterRecvResource(MCS_CHANNEL0);

        NNS_FndFreeToExpHeap(gAppHeap, recvBuf);        // Secure buffer for receiving data
        NNS_FndFreeToExpHeap(gAppHeap, printBuffer);    // Secure buffer for printing

        // Output with NNS_McsPutString
        (void)NNS_McsPutString("device close\n");

        // Close the device
        (void)NNS_McsClose();
    }
    else
    {
        OS_Printf("device open fail.\n");
        MainLoop(pPos);
    }

    NNS_FndFreeToExpHeap(gAppHeap, pPos);           // Release the memory that was secured for data

    while (TRUE) {}
}


