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

  Module: main.cpp

  Author: John Buckley

  Description:

    main startup

  Copyright 2005 Sony Online Entertainment.  All rights reserved.

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

/*
**
**	 Includes
**
*/

#include<stdio.h>
#include<string.h>
#include"SyTime.h"
#include"SyDebug.h"
#include"SyRaster.h"
#include"SyCollide.h"
#include"SyDictionary.h"
#include"SyCamera.h"
#include"SyScene.h"
#include"SyFlyCamera.h"
#include"SyESFParse.h"
#include"testwindow.h"
#include"kb.h"

/**/

/*
**
**	 Constants
**
*/

/**/


/*
**
**	 Defines
**
*/

#define SCREEN_WIDTH      1024
#define SCREEN_HEIGHT     768

#define CAMERA_VIEW_ANGLE 60.0f
#define CAMERA_NEAR       0.1f
#define CAMERA_FAR        1000.0f

#define SYCAMERAPATH_MAX_SAMPLES 100

//#define RENDERTARGET

/**/

/*
**
**	 Type Definitions
**
*/

/**/

/*
**
**	 Function Prototypes
**
*/

int LoadLevel( 
      const char8*  pPathname, 
      SyScene&      Scene );

int LoadLevel( 
      int32         NumSegments,
      const char8*  pPathname, 
      SyScene&      Scene );

int CheckSum( 
      int32         BufferSize,
      uint8         *pBuffer );

void UpdateInput( 
      SyCamera&     Camera, 
      SyFlyCamera&  FlyCamera,
      KB&           Keyboard, 
      SyTime        Time );

void DrawFullscreenQuad();

/**/

SyRaster Raster; // Global because of stack size situation
SyScene  Scene;  // Global because of stack size situation

static int s_nAvgBatchCount = 0;
static int s_nPeakBatchCount = 0;
static int s_nAvgTriCount = 0;
static int s_nPeakTriCount = 0;

/******************************************************************/
/* main                                                           */
/******************************************************************/

bool SetupSPUs( uint32_t targetSPUCount );

int main()
 {
  int32        Frame = 0;
  void*        hWnd = NULL;
  SyCamera     Camera;
  SyFlyCamera  FlyCamera;
  SyCollide    Collide;
  SyDictionary Dictionary;
  SyTime       Time;
  SyTime       Delta;  
  KB           Keyboard;

  SetupSPUs( 4 );

  /* Create the window */
  
  hWnd = InitWindow( SCREEN_WIDTH, SCREEN_HEIGHT );

  /* Initialize the keyboard */

  Keyboard.Init();

  /* Initialize the timer */

  SyTimer::Init();

  /* Set the shader directory */

#ifdef _WIN32
  Raster.SetShaderPath( "..\\..\\programming\\shaders\\" );
#else
  Raster.SetShaderPath( "../../programming/shaders/" );
#endif

  /* Initialize the raster device */

  if(Raster.Init( hWnd, SCREEN_WIDTH, SCREEN_HEIGHT, 0 ) < 0)
   {
    return(-1);
   }
  
  /* Initialize the collision device */

  if(Collide.Init() < 0)
   {
    return(-1);
   }

  /* Initialize the scene */

  if(Scene.Init( (SyTime)(0), Dictionary, &Raster, NULL, Collide ) < 0)
   {
    return(-1);
   }

  /* Initialize the camera */

  Camera.Init( Raster, 
               SyVect3( 5.0f, 5.0f, 5.0f ), 
               SyVect3( -5.0f, -5.0f, -5.0f ),
               CAMERA_VIEW_ANGLE,
               (float32)(SCREEN_WIDTH) / (float32)(SCREEN_HEIGHT),
               CAMERA_NEAR,
               CAMERA_FAR );

  FlyCamera.Init( Camera, Scene );

  /* Clear buffer */

  Raster.BeginScene();
  Raster.EndScene();
  Raster.BeginScene();
  Raster.EndScene();

#ifdef RENDERTARGET
  /* Create the render target texture */

  int32 RendSurfaceHandle = Raster.CreateSurface(0);
  SySurface* pSurface = Raster.Surface(RendSurfaceHandle);
  pSurface->Init(Raster, 1024, 768, SYTEXTFORMAT_RGBA32, 1, 1);

  /* Create the depth target texture */

  int32 DepthSurfaceHandle = Raster.CreateSurface(0);
  pSurface = Raster.Surface(DepthSurfaceHandle);
  pSurface->Init(Raster, 1024, 768, SYTEXTFORMAT_D24S8, 1, 1);

  /* Create a material to use for blatting the rt to the screen */

  uint32 MaterialHandle = Raster.CreateMaterial( SYMATERIALTYPE_SIMPLE, 0 );
  SySimpleMaterial* pMaterial = (SySimpleMaterial*)(Raster.Material( MaterialHandle ));
  pMaterial->SetTexture( RendSurfaceHandle, Raster, Dictionary );

  SyColor32F RenderTargetClearColor(0,0,0,1);
#endif

  /* Load the level */

  //LoadLevel( 14, "data/Hells_Kitchen_%02d.seg", Scene );
  LoadLevel( "data/flatplane.esf", Scene );

  /* Get the frame start time */

  Delta = SyTimer::GetTime();

  /* Render */
  
  while(1)
   {

       void SPUExec( void );

       SPUExec();

    /* Update the message loop */

    MessagePump();

    /* Update time */

    Time = SyTimer::GetTime();
    Scene.SetTime( Time );

    /* Update the keyboard */

    Keyboard.Update();

#ifdef _WIN32
    if(Keyboard.IsKeyDown(KB_ESC))
     {
      break;
     }
#endif

    /* Begin Frame */

    Raster.BeginScene();

    /* Get input */

    UpdateInput(Camera, FlyCamera, Keyboard, Time);

#ifdef RENDERTARGET
    /* Set the render target as active */

    Raster.SetRenderTarget(RendSurfaceHandle, RenderTargetClearColor);

    /* Set the depth target as active */

    Raster.SetDepthTarget(DepthSurfaceHandle);
#endif

    /* Draw */

    Scene.Render( Camera );

#ifdef RENDERTARGET
    /* Restore the backbuffer */

    Raster.SetRenderTarget();
    Raster.SetDepthTarget();
    
    /* Blat the RT to the screen */

    Raster.Begin2D();
    Raster.SetMaterial( MaterialHandle );
    DrawFullscreenQuad();
    Raster.End2D();
#endif

    /* End Frame */

    Raster.EndScene();

    /* Performance Metrics */

    s_nAvgBatchCount += Raster.GetPerfNumBatches();
    if(Raster.GetPerfNumBatches() > s_nPeakBatchCount)
     {
      s_nPeakBatchCount = Raster.GetPerfNumBatches();
     }
    s_nAvgTriCount += Raster.GetPerfNumTriangles();
    if(Raster.GetPerfNumTriangles() > s_nPeakTriCount)
     {
      s_nPeakTriCount = Raster.GetPerfNumTriangles();
     }

    if((Frame % 100) == (100 - 1))
     {
      int FPS;
      Delta = -Delta + SyTimer::GetTime();

      if(Delta != 0)
       {
        FPS = (int)((SyTime)(1000 * 100) / (Delta));
       }
      else  
       {
        FPS = 1000;
       }
  
      s_nAvgBatchCount = s_nAvgBatchCount / 100;
      s_nAvgTriCount = s_nAvgTriCount / 100;

#if _WIN32
      char8 String[256];
      sprintf(String, "FPS: %03d AvgBatchCount: %03d PeakBatchCount: %03d AvgTriCount: %03d PeakTriCount: %03d\n", FPS, s_nAvgBatchCount, s_nPeakBatchCount, s_nAvgTriCount, s_nPeakTriCount );
      OutputDebugString( String );
#else
      //printf("FPS: %03d AvgBatchCount: %03d PeakBatchCount: %03d AvgTriCount: %03d PeakTriCount: %03d\n", FPS, s_nAvgBatchCount, s_nPeakBatchCount, s_nAvgTriCount, s_nPeakTriCount );
#endif
      Delta = SyTimer::GetTime();

      s_nAvgBatchCount = 0;
      s_nPeakBatchCount = 0;
      s_nAvgTriCount = 0;
      s_nPeakTriCount = 0;
     }    

    Frame++;

#ifdef SY_GLES_RASTER
    /* Check for errors */

    if(glGetError()!= GL_NO_ERROR)
     {
      printf("GL Error\n");
     }
    CGerror Error = cgGetError();
    if(Error != CG_NO_ERROR)
     {
      printf("CG Error: %s\n", cgGetErrorString(Error));
     }
#endif
   }

  /* Clear the scene */

  if(Scene.Clear() < 0)
   {
    return(-1);
   }

  /* Shutdown the collision device */

  if(Collide.Close() < 0)
   {
    return(-1);
   }

  /* Shutdown the raster device */

  if(Raster.Close() < 0)
   {
    return(-1);
   }

  return(0);
 }

/******************************************************************/
/* LoadLevel                                                      */
/******************************************************************/

int LoadLevel( 
     const char8*  pPathname,
     SyScene&      Scene ) 
 {
  SyESFParse Parse;

  Parse.ParseWorld( pPathname, 0, Scene );
  
  return(0);
 }

/******************************************************************/
/* LoadLevel                                                      */
/******************************************************************/

int LoadLevel( 
     int32         NumSegments,
     const char8*  pPathname,
     SyScene&      Scene ) 
 {
  int32             I;
  int32             SegmentSize = (5 * 1024 * 1024);
  SyESFParse        Parse;
  SyFile            SegmentFile;
  SyObjFile         WorldFile;
  char8             SegmentFileName[256];
  SyFile :: MemSegm *pSegments;

  /* Allocate the segments */

  pSegments = SyNew SyFile::MemSegm[NumSegments];
  for(I = 0; I < NumSegments; I++)
   {
    pSegments[I].Size  = SegmentSize;
    pSegments[I].pData = SyNew uint8[SegmentSize];  
    memset(pSegments[I].pData, 0, SegmentSize);  
   }

  /* Load the segments */

  for(I = 0; I < NumSegments; I++)
   {
    sprintf(SegmentFileName, pPathname, I);
    if(SegmentFile.Open( SegmentFileName, SYFILE_RONLY | SYFILE_BINARY ) < 0)
     {
      printf("Error opening file <%s>\n", SegmentFileName );
      return(-1);
     }
      
    SegmentFile.Read( pSegments[I].pData, SegmentSize );
    printf("File <%s> checksum is %08x\n", 
            SegmentFileName, 
            CheckSum(SegmentSize, (uint8*)(pSegments[I].pData)));

    if(SegmentFile.Close() < 0)
     {
      return(-1);
     }
   }

  /* Parse the world */

  if(WorldFile.Open( NumSegments, pSegments, NumSegments * SegmentSize, SyObjFile::ROnly ) < 0)
   {
    return(-1);
   }

  if(Parse.Parse( WorldFile, Scene ) < 0)
   {
    printf("Error reading segmented level file\n");
   }
  
  if(WorldFile.Close() < 0)
   {
    return(-1);
   }

  /* Deallocate the segments */

  for(I = 0; I < NumSegments; I++)
   {
    delete [] (uint8*)(pSegments[I].pData);
   }
  delete [] pSegments;

  return(0);
 }

/******************************************************************/
/* CheckSum                                                       */
/******************************************************************/

int CheckSum( int32 BufferSize, uint8 *pBuffer )
 {
  int32 I;
  int32 Sum = 0;

  for(I = 0; I < BufferSize; I++)
   {
    Sum += pBuffer[I];
   }

  return(Sum);
 }

/******************************************************************/
/* UpdateInput
/******************************************************************/

void UpdateInput( SyCamera& Camera, SyFlyCamera& FlyCamera, KB& Keyboard, SyTime Time )
 {
  float32 Forward       = 0.0f;
  float32 TurnLeft      = 0.0f;
  float32 PitchDown     = 0.0f;
  float32 HoverUp       = 0.0f;
  float32 HoverForward  = 0.0f;
  float32 HoverStrafe   = 0.0f;

  Forward      = Keyboard.IsKeyDown(KB_UP) - Keyboard.IsKeyDown(KB_DOWN);
  TurnLeft     = Keyboard.IsKeyDown(KB_LEFT) - Keyboard.IsKeyDown(KB_RIGHT);
  PitchDown    = Keyboard.IsKeyDown(KB_PAGE_DOWN) - Keyboard.IsKeyDown(KB_PAGE_UP);
  HoverUp      = Keyboard.IsKeyDown(KB_HOME) - Keyboard.IsKeyDown(KB_END);
  HoverForward = Keyboard.IsKeyDown('W') - Keyboard.IsKeyDown('S');
  HoverStrafe  = Keyboard.IsKeyDown('D') - Keyboard.IsKeyDown('A');

  FlyCamera.Forward( Forward, 0.0f );
  FlyCamera.TurnLeft( TurnLeft );
  FlyCamera.PitchDown( PitchDown );
  FlyCamera.Hover( HoverForward, HoverStrafe, HoverUp );

  FlyCamera.Process( Camera, Scene );

  return;
 }


/******************************************************************/
/* DrawFullscreenQuad
/******************************************************************/

void DrawFullscreenQuad()
 {
	uint16* pIndices;
	SyVertexVUNC* pVertices;
	Raster.BeginIndexTris(SYVERTEXTYPE_VUNC, 2, 4, &pIndices, (void**)&pVertices);
	pVertices[0].V.X	= -1.0f;
	pVertices[0].V.Y	= -1.0f;
	pVertices[0].V.Z	= 1.0f;
	pVertices[0].C = SyColor32(255, 255, 255, 255);
	pVertices[0].UV0.X	= 0.0f;
	pVertices[0].UV0.Y	= 0.0f;
	pVertices[1].V.X	= 1.0f;
	pVertices[1].V.Y	= -1.0f;
	pVertices[1].V.Z	= 1.0f;
	pVertices[1].C = SyColor32(255, 255, 255, 255);
	pVertices[1].UV0.X	= 1.0f;
	pVertices[1].UV0.Y	= 0.0f;
	pVertices[2].V.X	= 1.0f;
	pVertices[2].V.Y	= 1.0f;
	pVertices[2].V.Z	= 1.0f;
	pVertices[2].C = SyColor32(255, 255, 255, 255);
	pVertices[2].UV0.X	= 1.0f;
	pVertices[2].UV0.Y	= 1.0f;
	pVertices[3].V.X	= -1.0f;
	pVertices[3].V.Y	= 1.0f;
	pVertices[3].V.Z	= 1.0f;
	pVertices[3].C = SyColor32(255, 255, 255, 255);
	pVertices[3].UV0.X	= 0.0f;
	pVertices[3].UV0.Y	= 1.0f;

	pIndices[0] = 0;
	pIndices[1] = 2;
	pIndices[2] = 1;
	pIndices[3] = 0;
	pIndices[4] = 3;
	pIndices[5] = 2;

	Raster.EndIndexTris();
 }

/* End of File */
