/*---------------------------------------------------------------------------*
  Project:  NitroSDK - demos - tp-1
  File:     calibrate.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: calibrate.c,v $
  Revision 1.5  2004/11/05 12:42:40  takano_makoto
  Added a notice relating to calibrate.c.

  Revision 1.4  2004/11/02 08:47:41  takano_makoto
  fix comment.

  Revision 1.3  2004/11/02 08:44:06  takano_makoto
  swap MAIN/SUB display.

  Revision 1.2  2004/09/24 09:24:05  takano_makoto
  Changed to use default value in chattering parameter. Deleted TP_RequestSetupStability.

  Revision 1.1  2004/05/21 09:56:20  takano_makoto
  Get calibration value from NVRAM

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

#include    <nitro.h>
#include    <nitro/spi/ARM9/tp.h>
#include    "data.h"
#include    "calibrate.h"

//============================================================================
// calibrate.c is for operating touch panel samples on hardware
// such as the TEG board and the TS board that can not acquire owner information.
// Normally, it is not required when operating with hardware such as
// the DS system or IS-NITRO-EMULATOR in which the start menu has been updated.
//===========================================================================



/*---------------------------------------------------------------------------*
    Static variables definition
 *---------------------------------------------------------------------------*/
static GXOamAttr    gOam[128];

/*---------------------------------------------------------------------------*
    Prototype definition
 *---------------------------------------------------------------------------*/
static void VBlankIntr(void);



/*---------------------------------------------------------------------------*
  Name:         GetSamplePointNow

  Description:  Get touched point by OneTime Sampling.
                This function use TP_RequestSampling() and TP_WaitRawResult()

  Arguments:    None.

  Returns:      data - gotten TouchPanel data.
                BOOL - if touched and got point this function returns TRUE.
                       else  FALSE.
 *---------------------------------------------------------------------------*/
static BOOL GetSamplePointNow(TPData *data)
{
    static u16 count  = 0;
    static u16 last_x = 0;
    static u16 last_y = 0;
    TPData temp;

    enum {
        OK_COUNT = 4, OK_RANGE = 50
    };

    // Detect a point pushed during definite time.
    while (TP_RequestRawSampling( &temp )) {
    };

    if (! temp.touch ) {
        count = 0;
        return FALSE;
    }

    if ( temp.validity != TP_VALIDITY_VALID ) {
        count = 0;
        return FALSE;
    }

    OS_Printf("( %d, %d )\n", temp.x, temp.y);

    count++;
    if ( count == 1 ) {
        last_x = temp.x;
        last_y = temp.y;
        return FALSE;
    }

    // if jump point from last frame, reset count.
    if ( (s32)(last_x - temp.x) < - OK_RANGE ||
         (s32)(last_x - temp.x) > OK_RANGE )
    {
        count = 1;
        last_x = temp.x;
        last_y = temp.y;
        return FALSE;
    }

    if ( (s32)(last_y - temp.y) < - OK_RANGE ||
         (s32)(last_y - temp.y) > OK_RANGE )
    {
        count = 1;
        last_x = temp.x;
        last_y = temp.y;
        return FALSE;
    }

    // if the point pressed during OK_COUNT, detect finish.
    if ( count == OK_COUNT ) {
        data->x = (u16) ( (temp.x + last_x) / 2 );
        data->y = (u16) ( (temp.y + last_y) / 2 );
        data->touch = TP_TOUCH_ON;
        data->validity = TP_VALIDITY_VALID;
        return TRUE;
    }

    last_x = temp.x;
    last_y = temp.y;
    return FALSE;
}


/*---------------------------------------------------------------------------*
  Name:         WaitPanelReleaseNow

  Description:  Wait to released TouchPanel, using OneTime Sampling.
                This function is using TP_RequestSampling() and TP_WaitRawResult().

  Arguments:    None.

  Returns:      BOOL  - if TouchPanel is released , this returns TRUE.
                else FALSE.

 *---------------------------------------------------------------------------*/
static BOOL WaitPanelReleaseNow( void )
{
    TPData temp;
    static u16 count = 0;

    enum {
        INTERVAL_CNT = 10
    };

    while (TP_RequestRawSampling( &temp )) {};

    if ( temp.touch ) {
        count = 0;
        return FALSE;
    }

    count++;
    if ( count >= INTERVAL_CNT ) {
        count = 0;
        return TRUE;
    } else {
        return FALSE;
    }
}


/*---------------------------------------------------------------------------*
  Name:         SetPoint8x8

  Description:  Display a 8x8 OBJ on indicated point.

  Arguments:    x - position X.
                y - position Y.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static inline void SetPoint8x8(u16 pos_x, u16 pos_y)
{
    G2_SetOBJAttr( &gOam[0],                 // OAM number
                   pos_x - 4,                // X position
                   pos_y - 4,                // Y position
                   0,                        // Priority
                   GX_OAM_MODE_BITMAPOBJ,    // Bitmap mode
                   FALSE,                    // mosaic off
                   GX_OAM_EFFECT_NONE,       // affine off
                   GX_OAM_SHAPE_8x8,         // 8x8 size
                   GX_OAM_COLOR_16,          // 16 color
                   0,                        // character
                   15,                       // alpha
                   0);
}


/*---------------------------------------------------------------------------*
  Name:         DisplayInit

  Description:  Graphics Initialization

  Arguments:    None.

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void Calibrate_DisplayInit()
{
    GX_Init();
    FX_Init();

    GX_DispOff();
    GXS_DispOff();

    OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
    (void)OS_EnableIrqMask( OS_IE_V_BLANK );
    (void)OS_EnableIrq();
    (void)GX_VBlankIntr(TRUE);         // to generate VBlank interrupt request
    GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);

    GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);
    MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);

    (void)GX_DisableBankForLCDC();

    MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);       // clear OAM
    MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);         // clear the standard palette

    MI_CpuFillFast((void*)HW_DB_OAM, 192, HW_DB_OAM_SIZE);  // clear OAM
    MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);   // clear the standard palette

    GX_SetBankForOBJ(GX_VRAM_OBJ_256_AB);                   // Set VRAM-A,B for OBJ

    GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS,                // 2D / 3D Mode
                       GX_BGMODE_0,                         // BGMODE 0
                       GX_BG0_AS_2D);                       // Set BG0 as 2D

    GX_SetVisiblePlane(GX_PLANEMASK_OBJ);                   // make OBJ visible
    GX_SetOBJVRamModeBmp(GX_OBJVRAMMODE_BMP_1D_128K);       // 2D mapping OBJ

    /* Load character bitmap data */
    GX_LoadOBJ( (const void *)IMAGE_DATA, 0, IMAGE_DATA_SIZE ); // Transfer OBJ bitmap data to VRAM

    SVC_WaitVBlankIntr();                                   // Waiting the end of VBlank interrupt
    GX_DispOn();

}

/*---------------------------------------------------------------------------*
  Name:         ExecGetCalibrate

  Description:  Get calibration parameter from 2 coordinates.

  Arguments:    None.

  Returns:      param - a pointer for store calibration parameter.
 *---------------------------------------------------------------------------*/
void ExecGetCalibrate( TPCalibrateParam *param )
{
    enum {
        SEQ_CALIBRATE_1, SEQ_INTERVAL_1,
        SEQ_CALIBRATE_2, SEQ_INTERVAL_2,
        SEQ_CHECK_PARAM, SEQ_INTERVAL_3,
        SEQ_GET_POINT, SEQ_INTERVAL_4,
        VCOUNT_START = 0, VCOUNT_FREQUENCE = 5
    };

    u32 seq = SEQ_CALIBRATE_1;  // Calibration Sequence state
    TPData sample1;
    TPData sample2;
    TPData sample3;
    TPData dispPos;
    TPCalibrateParam calibrate;

    Calibrate_DisplayInit();

    while ( TRUE )
    {
        switch (seq) {
        case SEQ_CALIBRATE_1:
        // detect first point.
            SetPoint8x8(32, 32);
            if ( GetSamplePointNow(&sample1) ) {
                OS_Printf("OK! ( %d, %d )\n", sample1.x, sample1.y);
                seq = SEQ_INTERVAL_1;
            }
            break;

        case SEQ_INTERVAL_1:
        // wait release TouchPanel
            if ( WaitPanelReleaseNow() ) {
                seq = SEQ_CALIBRATE_2;
            }
            break;

        case SEQ_CALIBRATE_2:
        // detect second point.
            SetPoint8x8( GX_LCD_SIZE_X - 32, GX_LCD_SIZE_Y - 32 );
            if ( GetSamplePointNow(&sample2) ) {
                OS_Printf("OK! ( %d, %d )\n", sample2.x, sample2.y);

                // Calculate and set calibration parameter from two detected point.
                if ( TP_CalcCalibrateParam( &calibrate,
                                            sample1.x, sample1.y,
                                            32, 32,
                                            sample2.x, sample2.y,
                                            GX_LCD_SIZE_X - 32, GX_LCD_SIZE_Y - 32 )
                     != 0 )
                {
                    OS_Printf("Illegal calibrate parameter\n");
                    seq = SEQ_CALIBRATE_1;
                    break;
                }

                TP_SetCalibrateParam( &calibrate );

                OS_Printf("Calibrate param: \n");
                OS_Printf("\tx0 = %d, xDotSize = %d\n", calibrate.x0, calibrate.xDotSize / 0x100);
                OS_Printf("\ty0 = %d, yDotSize = %d\n", calibrate.y0, calibrate.yDotSize / 0x100);
                OS_Printf("Check calibrate param\n");

                seq = SEQ_INTERVAL_2;
            }
            break;

        case SEQ_INTERVAL_2:
        // Wait release TouchPanel
            if ( WaitPanelReleaseNow() ) {
                seq = SEQ_CHECK_PARAM;
            }
            break;

        case SEQ_CHECK_PARAM:
        // Verify Calibration Parameter.
            SetPoint8x8( GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2 );
            if ( GetSamplePointNow(&sample3) ) {
                s32 xRange, yRange;

                OS_Printf("OK! ( %d, %d )\n", sample3.x, sample3.y);
                TP_GetUnCalibratedPoint( &dispPos.x, &dispPos.y, GX_LCD_SIZE_X / 2, GX_LCD_SIZE_Y / 2 );

                xRange = sample3.x - dispPos.x;
                yRange = sample3.y - dispPos.y;

                // if margin is too big, detect points again.
                if ( xRange > 100 || xRange < -100 || yRange > 100 || yRange < -100) {
                    OS_Printf("( %d, %d ) NG Resampling\n", dispPos.x, dispPos.y);
                    seq = SEQ_INTERVAL_4;
                    break;
                }

                OS_Printf("Raw ( %d, %d )\n", dispPos.x, dispPos.y);
                OS_Printf("\txRange = %d, yRange = %d\n", xRange, yRange);

                *param = calibrate;
                seq = SEQ_CALIBRATE_1;
                return;
            }
            break;

        case SEQ_INTERVAL_4:
        // Wait release TouchPanel
            if ( WaitPanelReleaseNow() ) {
                seq = SEQ_CALIBRATE_1;
            }
            break;
        }

        /* flush cache of OAM buffers to main memory */
        DC_FlushRange( gOam, sizeof(gOam) );

        GX_LoadOAM( gOam, 0, sizeof(gOam) );
        MI_DmaFill32( 3, gOam, 192, sizeof(gOam) );   // Clear OAM buffer

        // Wait V-Blank interrupt
        SVC_WaitVBlankIntr();
    }
}



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

  Description:  V-Blank function

  Arguments:    None.

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

    // Set IRQ check flag
    OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}

