///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// schism.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
#include "efx.h"
#include "ParticleLogo.h"
#include "Timer.h"
#include "Blurer.h"
#include "ImageQuad.h"
#include "TitleScreen.h"
#include "Tunnel.h"
#include "BigBang.h"
#include "Volcanic.h"
#include "Galaxy.h"
#include "Flora.h"
#include "SolarSystemCreation.h"
#include "MeteorAproaching.h"
#include "SolarSystem.h"
#include "BlackHole.h"
#include "Atmosphere.h"

#include "bass.h"

#define MAX_LOADSTRING	100
#define	LOGFAIL( s )	logstream << "failed" << endl << "exception caught: " << (s) << endl

// Global Variables:
HINSTANCE			hInst;													// current instance
HWND				hWnd;
TCHAR				szTitle[MAX_LOADSTRING];								// The title bar text
TCHAR				szWindowClass[MAX_LOADSTRING];							// The title bar text

// DX objects

PDIRECT3D8			g_pD3D = NULL;
PDIRECT3DDEVICE8	g_pDevice = NULL;
DWORD				g_pFrames = 0;

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
HRESULT				InitD3D( HWND );
HRESULT				CleanUp();
//BOOL				InitEfx();
//BOOL				Render();

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{ 	
	MSG msg;

	ShowCursor( FALSE );

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_SCHISM, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	CTimer*					timer = new CMultimediaTimer();
	CParticleLogo*			particleLogo = NULL;		
	CBlurer*				blur = NULL;		
	CTitleScreen*			titleScreen = NULL;
	CTunnel*				tunnel = NULL;
	CBigBang*				bigbang = NULL;
	CVolcanic*				volcanic = NULL;
	CGalaxy*				galaxy = NULL;
	CFlora*					flora = NULL;
	CSolarSystemCreation*	creation = NULL;
	CMeteorAproaching*		meteor = NULL;
	CSolarSystem*			solarsystem = NULL;
	CBlackHole*				blackhole = NULL;
	CAtmosphere*			clouds = NULL;

	HSTREAM					track;

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
		return FALSE;

	logstream.open( "log.txt" );

	logstream << "shism demo logfile (c)zone51 2001" << endl;	
	logstream << endl << endl;	
	logstream << "·bass initialization: ";

	if( !BASS_Init( -1, 44100, 0, 0 ) )
	{
		logstream << "failed" << endl;
		return FALSE;
	}

	if( ! ( track = BASS_StreamCreateFile( FALSE, "track.mp3", 0, 0, 0 ) ) )
	{
		logstream << "failed" << endl;
		return FALSE;
	}

	BASS_Start();
	BASS_SetVolume( 80 );

	logstream << "succeeded" << endl;

	logstream << "·system initialization: ";

	if( FAILED( InitD3D( hWnd ) ) )	
	{	
		DestroyWindow( hWnd );
		return FALSE;
	}	

	try
	{	
		blur = new CBlurer( g_pDevice );
		timer->Init();
		logstream << "succeeded" << endl;

		logstream << "·particle logo efx initialization: ";
		particleLogo = new CParticleLogo( g_pDevice );	
		logstream << "succeeded" << endl;
		logstream << "·title screen efx initialization: ";
		titleScreen = new CTitleScreen( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·tunnel efx initialization: ";
		tunnel = new CTunnel( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·bigbang efx initialization: ";
		bigbang = new CBigBang( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·volcanic scene initialization: ";
		volcanic = new CVolcanic( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·galaxy efx initialization: ";
		galaxy = new CGalaxy( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·flora scene initialization: ";
		flora = new CFlora( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·solar system creation efx initialization: ";
		creation = new CSolarSystemCreation( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·meteor approaching efx initialization: ";
		meteor = new CMeteorAproaching( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·solar system efx initialization: ";
		solarsystem = new CSolarSystem( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·solar system efx initialization: ";
		blackhole = new CBlackHole( g_pDevice );
		logstream << "succeeded" << endl;
		logstream << "·atmosphere efx initialization: ";
		clouds = new CAtmosphere( g_pDevice );
		logstream << "succeeded" << endl;

	}	
	catch( CSystemException e )
	{
		LOGFAIL( e.GetMessage() );

		if( particleLogo )
			delete particleLogo;

		if( titleScreen )
			delete titleScreen;

		if( tunnel )
			delete tunnel;

		if( blur )
			delete blur;

		delete timer;
		CleanUp();
		DestroyWindow( hWnd );		

		return FALSE;
	}	
		
	g_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0x0, 1.0f, 0 );		
	timer->Start();
	BASS_StreamPlay( track, FALSE, 0 );
	
	do
	{
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{			
			TranslateMessage(&msg);
			DispatchMessage(&msg);			
		}
		else
		{
			FLOAT			fTime = timer->GetCurrentSec();

			if( fTime <= 25.0f )
			{
				particleLogo->UpdateFrame( fTime );

				blur->BeginScene();
				particleLogo->RenderEfx();
				blur->EndScene();
			}
			else if( fTime <= 38.0f )
			{				
				titleScreen->UpdateFrame( fTime - 25.0f );			

				g_pDevice->BeginScene();						
				titleScreen->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime <= 59.0f )
			{			
				tunnel->UpdateFrame( fTime - 38.0f );			

				g_pDevice->BeginScene();						
				tunnel->RenderEfx();
				g_pDevice->EndScene();
			} 
			else if( fTime <= 74.0f )
			{						
				bigbang->UpdateFrame( fTime - 59.0f );			

				blur->BeginScene();
				bigbang->RenderEfx();
				blur->EndScene();
			}			
			else if( fTime <= 94.0f )
			{
				galaxy->UpdateFrame( fTime - 74.0f );			

				g_pDevice->BeginScene();						
				galaxy->RenderEfx();
				g_pDevice->EndScene();
			}	
			else if( fTime <= 111.0f )
			{
				creation->UpdateFrame( fTime - 94.0f );
				blur->BeginScene();
				creation->RenderEfx();
				blur->EndScene();
			}
			else if( fTime <= 128.0f )
			{
				meteor->UpdateFrame( fTime - 111.0f );
				g_pDevice->BeginScene();
				meteor->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime <= 135.0f )
			{
				clouds->UpdateFrame( fTime - 128.0f );
				g_pDevice->BeginScene();
				clouds->RenderEfx();
				g_pDevice->EndScene();
			}
			else if( fTime <= 165.0f )
			{		
				volcanic->UpdateFrame( fTime - 135.0f );			

				g_pDevice->BeginScene();						
				volcanic->RenderEfx();
				g_pDevice->EndScene();
				
			} 
			else if( fTime <= 189.0f )
			{			
				flora->UpdateFrame( fTime - 165.0f );
				blur->BeginScene();
				flora->RenderEfx();
				blur->EndScene();
			}
			else if( fTime <= 205.0f )
			{			
				solarsystem->UpdateFrame( fTime - 189.0f );
				blur->BeginScene();
				solarsystem->RenderEfx();
				blur->EndScene();
			}
			else if( fTime <= 230.0f )
			{
				blackhole->UpdateFrame( fTime - 205.0f );
				blur->BeginScene();
				blackhole->RenderEfx();
				blur->EndScene();
			}
			else
				break;			

			g_pFrames++;

			g_pDevice->Present( NULL, NULL, NULL, NULL );
		}

	}while ( msg.message != WM_QUIT );

	BASS_Stop();
	BASS_StreamFree( track );
	BASS_Free();
	
	FLOAT					fFPS = (FLOAT)g_pFrames/timer->GetCurrentSec();

	g_pDevice->SetStreamSource( 0, NULL, 0 );
	g_pDevice->SetIndices( NULL, 0 );
	g_pDevice->SetTexture( 0, NULL );
	g_pDevice->SetTexture( 0, NULL );
	
	delete particleLogo;
	delete titleScreen;
	delete tunnel;
	delete bigbang;
	delete volcanic;
	delete galaxy;
	delete flora;
	delete creation;
	delete meteor;
	delete solarsystem;
	delete clouds;
	delete blackhole;
	delete timer;
	
	CleanUp();										// succeeded up a direct3d objects

	logstream << "·exit: succeeded" << endl;
	logstream << endl << "·average fps: " << fFPS;
	logstream.close();

	ShowCursor( TRUE );
	
	return msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_ENABLE;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_SCHISM);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= NULL;
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

#ifdef _DEBUG
	hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
					    CW_USEDEFAULT, 0, 640, 480, NULL, NULL, hInstance, NULL);
#else
   hWnd = CreateWindow(szWindowClass, szTitle, WS_POPUP,
      0, 0, 0, 0, NULL, NULL, hInstance, NULL);
#endif

   if (!hWnd)   
      return FALSE;   

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{	
	switch (message) 
	{		
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_KEYDOWN:
			if( wParam == VK_ESCAPE )
			{			
				BASS_Stop();
				DestroyWindow( hWnd );			
			}
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

HRESULT InitD3D( HWND hWnd )
{	
	// create directGraphic interface
	if( !( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
	{			
		LOGFAIL( "unalbe to create d3d" );
		return E_FAIL;
	}	

	D3DPRESENT_PARAMETERS	d3dpp;

	ZeroMemory( &d3dpp, sizeof(D3DPRESENT_PARAMETERS) );

#ifdef _DEBUG
	d3dpp.Windowed			= TRUE;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_DISCARD;
#else
	d3dpp.Windowed			= FALSE;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_FLIP;
#endif
	d3dpp.BackBufferFormat	= D3DFMT_A8R8G8B8;	
	d3dpp.BackBufferCount	= 3;
	d3dpp.BackBufferWidth	= 640;
	d3dpp.BackBufferHeight	= 480;	
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
									  D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pDevice )	) )
	{
		LOGFAIL( "unable to create device" );
		return E_FAIL;
	}				

	g_pDevice->SetTransform( D3DTS_PROJECTION, ProjectionMtx( 15.0f, 0.75f, 0.5f, 1000.0f ) );
	g_pDevice->SetTransform( D3DTS_VIEW, CameraMtx( he3d_CVector( 0, 0, -3 ), he3d_CVector( 0, 0, 3 ), 0 ) );
	g_pDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
	g_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_FALSE );

	D3DMATERIAL8			d3dmat;
	ZeroMemory( &d3dmat, sizeof(D3DMATERIAL8) );

	d3dmat.Ambient.r = 1.0f;
	d3dmat.Ambient.g = 1.0f;
	d3dmat.Ambient.b = 1.0f;

	g_pDevice->SetMaterial( &d3dmat );

	InitializeTextureSystem( g_pDevice );
	SetTexturePath( "data\\textures\\" );
	
	return S_OK;				
}

HRESULT CleanUp()
{	
	if( g_pDevice )
		g_pDevice->Release();

	if( g_pD3D )
		g_pD3D->Release();

	CloseTextureSystem();

	return S_OK;
}



