
/****************************************************************************
 *
 * This file is a product of Criterion Software Ltd.
 *
 * This file is provided as is with no warranties of any kind and is
 * provided without any obligation on Criterion Software Ltd.
 * or Canon Inc. to assist in its use or modification.
 *
 * Criterion Software Ltd. and Canon Inc. will not, under any
 * circumstances, be liable for any lost revenue or other damages
 * arising from the use of this file.
 *
 * Copyright (c) 1999 Criterion Software Ltd.
 * All Rights Reserved.
 *
 */

/****************************************************************************
 *
 * main.c
 *
 * Copyright (C) 1999 Criterion Technologies.
 *
 * Original author: Adam Billyard.
 *
 * Purpose: RenderWare 3.0 demo.
 *
 ****************************************************************************/

#include "rwcore.h"

#ifdef RWLOGO
#include "rplogo.h"
#endif /* RWLOGO */

#include "rt2d.h"

#ifdef RWMETRICS
#include "metrics.h"
#endif

#include "camera.h"
#include "skeleton.h"
#include "menu.h"
#include "events.h"
#include "rttilerd.h"

#include "paint2d.h"
#include "main.h"

#define DEFAULT_CAMERA_WIDTH 640
#define DEFAULT_CAMERA_HEIGHT 480
#define DEFAULT_VIEWWINDOW (RwReal)(1.0)
#ifdef WIDE_SCREEN
/* We run the PS2 stuff on a widescreen TV usually */
#define DEFAULT_ASPECTRATIO     (RwReal)(16.0f/9.0f)
#else
#define DEFAULT_ASPECTRATIO     (RwReal)(4.0f/3.0f)
#endif

static RwReal ViewWindow = DEFAULT_VIEWWINDOW;

static RwUInt32 LastFrameTime;
static RwUInt32 LastAnimTime;

static RwV2d TranslateXStep;
static RwV2d TranslateYStep;
static RwV2d RotateOrigin;

static RwV2d Position;

static RwBool Animate = TRUE;
static RwBool ZoomIn = FALSE;
static RwBool ZoomOut = FALSE;

static RwCamera *Camera = NULL;

static void HandleClear(RwChar *args);
static void HandleAnimation(RwChar *args);
static void HandleReset(RwChar *args);
static void HandleTileRender(RwChar *args);

/*--- External Variables ---*/
#ifdef MENUING
extern RwChar executeElf[256];
#endif

RwInt32 WinWidth = DEFAULT_CAMERA_WIDTH;
RwInt32 WinHeight = DEFAULT_CAMERA_HEIGHT;

RwReal DeltaTime = 0.0f;
RwReal AnimationTime = 0.0f;

RwBool TileRender = FALSE;
RwReal WinAspect;
Rt2dBBox WinBBox;
RwBool DisplayConsole = FALSE;

/*
 *****************************************************************************
 */
static RwBool
OnOff2Bool(RwChar *string)
{
    if( string )
    {
        if( !rwstrcmp(string, RWSTRING("on")) )
        {
            return TRUE;
        }
        else if( !rwstrcmp(string, RWSTRING("off")) )
        {
            return FALSE;
        }
    }

    return FALSE;
}

/*
 *****************************************************************************
 */
static void
HandleClear(RwChar *args __RWUNUSED__)
{
    RsTerminalClear(RsGlobal.terminal);

    return;
}

/*
 *****************************************************************************
 */
static void
HandleAnimation(RwChar *args)
{
    if( args )
    {
        Animate = OnOff2Bool(args);
    }
    else
    {
        RsTerminalPrintf(RsGlobal.terminal, RWSTRING("animate: %s\n"),
            Animate ? RWSTRING("on") : RWSTRING("off"));
    }

    return;
}

/*
 *****************************************************************************
 */
static void
HandleReset(RwChar *args __RWUNUSED__)
{
    Rt2dCTMSetIdentity();

    return;
}

/*
 *****************************************************************************
 */
static void
HandleTileRender(RwChar *args)
{
    if( args )
    {
        RwRaster *camRaster;
        RwInt32   rasWidth, rasHeight;
        RwInt32 i, ix, iy, tx, ty;
        RwChar  filename[80];

        TileRender = TRUE;

        if( rwsscanf(args, "%s %d %d %d %d", filename, &ix, &iy, &tx, &ty) == 5)
        {
            RtTileRender(Camera, ix, iy, tx, ty, RenderFrame,
                RtTileDefaultArchive, filename);
        }
        else if (rwsscanf(args, "%s %d %d", filename, &ix, &iy) == 3)
        {
            camRaster = RwCameraGetRaster(Camera);

            rasWidth = RwRasterGetWidth(camRaster);
            rasHeight = RwRasterGetHeight(camRaster);

            i = 1;
            while (((ix / i) > rasWidth) || ((ix % i) > 0))
                i++;

            tx = ix / i;

           i = 1;
            while (((iy / i) > rasHeight) || ((iy % i) > 0))
                i++;

            ty = iy / i;

            RtTileRender(Camera, ix, iy, tx, ty,
                RenderFrame, RtTileDefaultArchive, filename);
        }
        else
        {
            RsTerminalPrintf(RsGlobal.terminal,
                RWSTRING("tilerender <RAS filename> <imgW> <imgH> [<tlW> <tlH>]\n"));
        }

        TileRender = FALSE;
    }
    else
    {
        RsTerminalPrintf(RsGlobal.terminal,
            RWSTRING("tilerender <RAS filename> <imgW> <imgH> [<tlW> <tlH>]\n"));
    }

    return;
}

/*
 *****************************************************************************
 */
void
PositionSet(RwReal x, RwReal y)
{
    Position.x = x;
    Position.y = y;

    return;
}

/*
 *****************************************************************************
 */
void
ZoomInToggle(RwBool value)
{
    ZoomIn = value;

    return;
}

/*
 *****************************************************************************
 */
void
ZoomOutToggle(RwBool value)
{
    ZoomOut = value;

    return;
}

/*
 *****************************************************************************
 */
void
PageTranslateInit(void)
{
    RwV2d temp;

    Rt2dDeviceGetStep(&TranslateXStep, &TranslateYStep, &temp);

    return;
}

/*
 *****************************************************************************
 */
void
PageTranslate(RwReal x, RwReal y)
{
    Rt2dCTMTranslate( x * TranslateXStep.x,  x * TranslateXStep.y);
    Rt2dCTMTranslate(-y * TranslateYStep.x, -y * TranslateYStep.y);

    return;
}

/*
 *****************************************************************************
 */
void
PageRotateInit(RwReal x, RwReal y)
{
    RwV2d xStep, yStep, origin;

    Rt2dDeviceGetStep(&xStep, &yStep, &origin);
    RwV2dScale(&xStep, &xStep, x);
    RwV2dScale(&yStep, &yStep, y);
    RwV2dAdd(&RotateOrigin, &xStep, &yStep);
    RwV2dAdd(&RotateOrigin, &RotateOrigin, &origin);

    return;
}

/*
 *****************************************************************************
 */
void
PageRotate(RwReal x)
{
    Rt2dCTMTranslate(RotateOrigin.x, RotateOrigin.y);
    Rt2dCTMRotate(x);
    Rt2dCTMTranslate(-RotateOrigin.x, -RotateOrigin.y);

    return;
}

/*
 *****************************************************************************
 */
void
ConsoleToggle(void)
{
    DisplayConsole = !DisplayConsole;

    RsTerminalSetState(RsGlobal.terminal,
        (DisplayConsole ? RSTERMINALSTATEACTIVE : RSTERMINALSTATEOFF));

    if (DTermLines)
    {
        DTermLines = -DTermLines;
    }
    else
    {
        DTermLines = TermLines ? -4 : 4;
    }

    return;
}

RsEventStatus
ConsoleEventHandler(RsEvent event, void *param)
{
    RsKeyStatus    *ks      = (RsKeyStatus *)param;
    RwInt32         keycode =                ks->keyCharCode;

    switch (event)
    {
    case rsKEYDOWN :
        if (keycode == rsESC)
        {
            RsEventHandler(rsQUITAPP, NULL);
            return ( rsEVENTPROCESSED );
        }
        if (keycode == rsF1)
        {
            ConsoleToggle();
            return ( rsEVENTPROCESSED );
        }
            
        if (DisplayConsole)
        {
            RwUInt8 key;
            
            key = RsKeyFromScanCode((RwUInt8)keycode, FALSE);
            RsTerminalUpdate(RsGlobal.terminal, key, NULL);
            return ( rsEVENTPROCESSED );
        }
    default:
        return ( rsEVENTNOTPROCESSED );
    }
}


/*
 *****************************************************************************
 */
static void
Terminate3D(void)
{
    MenuClose();

#ifdef RWMETRICS
    RsMetricsClose();
#endif /* RWMETRICS */

    if( Camera )
    {
        CameraDestroy(Camera);
    }

    Terminate2D();

#ifdef MENUING
#ifdef CDROM
    rwstrcpy(executeElf, "cdrom0:\\MENU.ELF");
#else
    rwstrcpy(executeElf, "host0:\\menu.elf");
#endif    
#endif

    RsRwTerminate();

    return;
}

/*
 *****************************************************************************
 */
static RwBool
InitializeTerminal(void)
{
    static RwChar _animate[] = RWSTRING("animate");
    static RwChar _clear[] = RWSTRING("clear");
    static RwChar _reset[] = RWSTRING("reset");
    static RwChar _tilerender[] = RWSTRING("tilerender");
    static RwChar _null[] = RWSTRING("");
    static RsTerminalCommandDictionaryEntry Commands[] =
    {
        { _animate,     HandleAnimation },
        { _clear,       HandleClear },
        { _reset,       HandleReset },
        { _tilerender,  HandleTileRender },
        { NULL,        NULL }
    };
    RwInt32 i;

    i=0;

    while( Commands[i].command )
    {
        if( !RsTerminalAddCommand(RsGlobal.terminal, &Commands[i]) )
        {
            return FALSE;
        }

        i++;
    };

    return TRUE;
}

/*
 *****************************************************************************
 */
static RwBool
Initialize3D(void *param)
{
    /*
     * Do the standard skeleton initialisation...
     */
    if( !RsRwInitialize(param) )
    {
        RsErrorMessage(RWSTRING("RsRwInitialize failed."));

        return FALSE;
    }

    /*
     * Use the RW skeleton to create a camera...
     */
    Camera = CameraCreate(RsGlobal.maximumWidth, RsGlobal.maximumHeight, TRUE);

    if( Camera == NULL )
    {
        RsErrorMessage(RWSTRING("Cannot create camera."));

        return FALSE;
    }
    else
    {
        RwV2d vw;

        vw.x = (RwReal)(1.0);
        vw.y = vw.x * RsGlobal.maximumHeight / RsGlobal.maximumWidth;
        RwCameraSetViewWindow(Camera, &vw);

        RwCameraSetNearClipPlane(Camera, (RwReal)(0.05));
        RwCameraSetFarClipPlane(Camera, (RwReal)(100.0));
    }

    /*
     * Initialize the 2D paint functionality...
     */
    if( !Initialize2D(Camera) )
    {
        RsErrorMessage(RWSTRING("Cannot initialize 2D components."));

        return FALSE;
    }

    /*
     * Initialize the terminal commands...
     */
    if( !InitializeTerminal() )
    {
        RsErrorMessage(RWSTRING("Cannot initialize terminal commands."));
    }

#ifdef RWMETRICS
    RsMetricsOpen(Camera);
#endif /* RWMETRICS */

    /*
     * Initialise menu
     */
    MenuOpen( TRUE, &FGNDColor, &BGNDColor );

    /*
     * Start with the menu off, not the default which is MENUMODE
     */
    MenuSetStatus( MENUOFF );

    return TRUE;
}

/*
 *****************************************************************************
 */
static RwBool
Initialize(void)
{
    /*
     * Application can override any of the skeleton default values here...
     */

#if (defined(SKY2_DRVMODEL_H))
    /* Yes we want proper tristrip clipping for this demo.
     * When you zoom in you need this. Rt2D submits strips a lot. */
    RpSkySelectTrueTSClipper(TRUE);
#endif /*(defined(SKY2_DRVMODEL_H))*/

    if( RsInitialize() )
    {
        if (!RsGlobal.maximumWidth && !RsGlobal.maximumHeight)
        {
            RsGlobal.maximumWidth  = DEFAULT_CAMERA_WIDTH;
            RsGlobal.maximumHeight = DEFAULT_CAMERA_HEIGHT;
        }

        RsGlobal.appName = RWSTRING("RW3 2D Paint Demo");

        LastAnimTime = LastFrameTime = RsTimer();

        return TRUE;
    }

    return FALSE;
}

/*
 *****************************************************************************
 */
static void
Idle(void)
{
    RwUInt32 thisTime;
    thisTime = RsTimer();

    if( thisTime < LastAnimTime )
    {
        DeltaTime = 0.0f;
    }
    else
    {
        if( Animate )
        {
            DeltaTime = (thisTime - LastAnimTime) * 0.001f;
            AnimationTime += DeltaTime;
        }
        else
        {
            DeltaTime = 0.0f;
        }
    }
    LastAnimTime = thisTime;

    /*
     * Animate terminal window...
     */
    TermLines += DTermLines;

    if( TermLines <= 0 )
    {
        TermLines = 0;
        DTermLines = 0;
    }

    if( TermLines >= (RsGlobal.terminal->height - 2) )
    {
        TermLines = (RsGlobal.terminal->height - 2);
        DTermLines = 0;
    }

    /* Animate View Zoom */
    if (ZoomIn)
    {
        RwV2d xStep, yStep, origin;

        Rt2dDeviceGetStep(&xStep, &yStep, &origin);

        RwV2dScale(&xStep, &xStep, Position.x);
        RwV2dScale(&yStep, &yStep, WinHeight - Position.y);

        Rt2dCTMTranslate(origin.x, origin.y);
        Rt2dCTMTranslate(xStep.x, xStep.y);
        Rt2dCTMTranslate(yStep.x, yStep.y);

        Rt2dCTMScale((RwReal)(1.03), (RwReal)(1.03));

        Rt2dCTMTranslate(-yStep.x, -yStep.y);
        Rt2dCTMTranslate(-xStep.x, -xStep.y);
        Rt2dCTMTranslate(-origin.x, -origin.y);
    }

    if (ZoomOut)
    {
        RwV2d xStep, yStep, origin;

        Rt2dDeviceGetStep(&xStep, &yStep, &origin);

        RwV2dScale(&xStep, &xStep, Position.x);
        RwV2dScale(&yStep, &yStep, WinHeight - Position.y);

        Rt2dCTMTranslate(origin.x, origin.y);
        Rt2dCTMTranslate(xStep.x, xStep.y);
        Rt2dCTMTranslate(yStep.x, yStep.y);

        Rt2dCTMScale((RwReal)(1.0) / (RwReal)(1.03), (RwReal)(1.0) / (RwReal)(1.03));

        Rt2dCTMTranslate(-yStep.x, -yStep.y);
        Rt2dCTMTranslate(-xStep.x, -xStep.y);
        Rt2dCTMTranslate(-origin.x, -origin.y);
    }

    /*
     * Render the next frame to see the changes or just
     * to do a repaint...
     */
    RenderFrame(Camera, 0, 0, NULL);

    return;
}

/*
 *****************************************************************************
 */
static RwBool
PluginAttach(void)
{
#ifdef RWLOGO
    if( !RpLogoPluginAttach() )
    {
        RsErrorMessage(RWSTRING("Logo plugin attach failed."));

        return FALSE;
    }
#endif /* RWLOGO */
#ifdef SKY
    if( !RpWorldPluginAttach() )
    {
        RsErrorMessage(RWSTRING("World plugin attach failed."));

        return FALSE;
    }
#endif
    return TRUE;
}


/*
 *****************************************************************************
 */
RsEventStatus
AppEventHandler(RsEvent event, void *param)
{
    /* Simulate a Mouse on Pad based platforms */

    switch( event )
    {
        case rsINITIALIZE:
        {
            return Initialize() ? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsCAMERASIZE:
        {
            RwVideoMode videoMode;

            CameraSize(Camera, param, ViewWindow, DEFAULT_ASPECTRATIO);

            RwEngineGetVideoModeInfo(&videoMode, RwEngineGetCurrentVideoMode());
            WinWidth = RwRasterGetWidth(RwCameraGetRaster(Camera));
            WinHeight = RwRasterGetHeight(RwCameraGetRaster(Camera));

            if (videoMode.flags & rwVIDEOMODEEXCLUSIVE)
            {
                /* Assume 4/3 Aspect Ratio */
                WinAspect = DEFAULT_ASPECTRATIO;
            }
            else
            {
                WinAspect = (RwReal)WinWidth / (RwReal)WinHeight;
            }
            Rt2dDeviceSetCamera(Camera);

            /* get the User space bbox of camera view */
            if (Camera)
            {
                Rt2dPath *path;

                Rt2dCTMPush();
                Rt2dCTMSetIdentity();
                path = Rt2dPathCreate();
                Rt2dDeviceGetClippath(path);
                Rt2dPathGetBBox(path, &WinBBox);
                Rt2dPathDestroy(path);
                Rt2dCTMPop();
            }

            return rsEVENTPROCESSED;
        }

        case rsRWINITIALIZE:
        {
            return Initialize3D(param) ? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsRWTERMINATE:
        {
            Terminate3D();

            return rsEVENTPROCESSED;
        }

        case rsPLUGINATTACH:
        {
            return PluginAttach() ? rsEVENTPROCESSED : rsEVENTERROR;
        }

        case rsINPUTDEVICEATTACH:
        {
            AttachInputDevices();

            return (rsEVENTPROCESSED);
        }

        case rsIDLE:
        {
            Idle();

            return rsEVENTPROCESSED;
        }

        default:
        {
            return rsEVENTNOTPROCESSED;
        }
    }
}
