// demopaja.cpp : Defines the class behaviors for the application.
//

#pragma warning( disable : 4786 )		// long names generated by STL

#include "stdafx.h"
#include "demopaja.h"
#include "mmsystem.h"
#include "DemopajaVersion.h"
#include "StartDlg.h"
#include "MainFrm.h"
#include "demopajaDoc.h"
#include "demopajaView.h"
#include "DeviceContextC.h"
#include "DeviceInterfaceI.h"
#include "GraphicsDeviceI.h"
#include "GraphicsViewportI.h"
#include "TimeContextC.h"
#include "opengldriver\OpenGLDriver.h"
#include "DocManagerEx.h"
#include "EffectPostProcessI.h"
#include "AudioSpectrumC.h"

//#include "logger\logger.h"

// internal effects/importer
#include "SubSceneC.h"

#include "fmod.h"
#include "fmod_errors.h"

#include "StartDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


using namespace PajaTypes;
using namespace PluginClass;
using namespace PajaSystem;
using namespace Composition;




/////////////////////////////////////////////////////////////////////////////
// CDemopajaApp

BEGIN_MESSAGE_MAP(CDemopajaApp, CWinApp)
	//{{AFX_MSG_MAP(CDemopajaApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG_MAP
	// Standard file based document commands
	ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDemopajaApp construction

CDemopajaApp::CDemopajaApp() :
	m_bFMODPresent( false ),
	m_rDefaultGraphicsDeviceId( NULL_CLASSID )
{
	// Get the current state of the flag
	// and store it in a temporary variable
/*	int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
	tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
	tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
	_CrtSetDbgFlag( tmpFlag );

// 64, 72
	uint32	ui32ERR = 72;

	_CrtSetBreakAlloc( ui32ERR );
	_crtBreakAlloc = ui32ERR;
*/

}

/////////////////////////////////////////////////////////////////////////////
// The one and only CDemopajaApp object

CDemopajaApp theApp;

FactoryC* CDemopajaApp::GetFactory()
{
	return &m_rFactory;
}

void* CDemopajaApp::FactoryCreate( const ClassIdC& rClassId )
{
	return m_rFactory.create( rClassId );
}


/////////////////////////////////////////////////////////////////////////////
// CDemopajaApp initialization

BOOL CDemopajaApp::InitInstance()
{
	// InitCommonControls() is required on Windows XP if an application
	// manifest specifies use of ComCtl32.dll version 6 or later to enable
	// visual styles.  Otherwise, any window creation will fail.
	InitCommonControls();

	CWinApp::InitInstance();

//	logger.logFile() = "demopaja.log";
//	logger.start( true );


	// Make sure we are using right DLL version.
	if( FSOUND_GetVersion() < FMOD_VERSION ) {
		CString	sErr;
		sErr.Format( "You are using the wrong DLL version!  You should be using FMOD %.02f\n", FMOD_VERSION );
		::MessageBox( NULL, sErr, "FMOD Error", MB_OK | MB_ICONERROR );
		return FALSE;
	}

	// Find the plugin directory
	char	szCurDir[_MAX_PATH];
	::GetCurrentDirectory( _MAX_PATH, szCurDir );

	m_sAppPath = szCurDir;

	m_sPluginPath = szCurDir;		// bugs with STL!!
	m_sPluginPath += "\\plugins\\";

	// regsiter build-in plugins
	m_rFactory.register_class( &g_rSceneImportDesc );
	m_rFactory.register_class( &g_rSceneEffectDesc );
	m_rFactory.register_class( &g_rSceneToImageImportDesc );
	m_rFactory.register_class( &g_rSharedBufferImportDesc );
	m_rFactory.register_class( &g_rAudioSpectrumDesc );

	// load plugins
	m_rFactory.load_plugins( m_sPluginPath.c_str() ); //"plugins" );

	// Init OLE
	if( !AfxOleInit() ) {
		AfxMessageBox( "Intialising OLE failed." );
		return FALSE;
	}
	AfxEnableControlContainer();


	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

	// Change the registry key under which our settings are stored.
	SetRegistryKey( _T("Moppi Productions") );

	LoadStdProfileSettings( 10 );  // Load standard INI file options (including MRU)


	// Get recent directories
	CString	sCurDir;
	sCurDir= GetProfileString( "Settings", "CurrentDirOpen", "" );
	m_sCurrentDir[0] = sCurDir;
	sCurDir= GetProfileString( "Settings", "CurrentDirSave", "" );
	m_sCurrentDir[1] = sCurDir;
	sCurDir= GetProfileString( "Settings", "CurrentDirImp", "" );
	m_sCurrentDir[2] = sCurDir;
	sCurDir= GetProfileString( "Settings", "CurrentDirExp", "" );
	m_sCurrentDir[3] = sCurDir;
	sCurDir= GetProfileString( "Settings", "CurrentDirColorSwatch", "Color Swatches\\VisiBone2.aco" );
	m_sCurrentDir[4] = sCurDir;


	CString	sClassName = GetProfileString( "Settings", "DefaultGraphicsDeviceName", "--" );
	CString	sClassId = GetProfileString( "Settings", "DefaultGraphicsDeviceID", "--" );

	if( sClassName != "--" || sClassId != "--" ) {
		// Store class name
		m_sDefaultGraphicsDeviceName = (const char*)sClassName;

		// Store class ID
		uint32	ui32A, ui32B;
		sscanf( (const char*)sClassId, "%x %x", &ui32A, &ui32B );
		m_rDefaultGraphicsDeviceId = ClassIdC( ui32A, ui32B );

		// Make sure the driver is loaded.
		ClassDescC*	pDesc = m_rFactory.get_classdesc( m_rDefaultGraphicsDeviceId );

		if( !pDesc || pDesc->get_classtype() != CLASS_TYPE_DEVICEDRIVER ||
			pDesc->get_super_class_id() != SUPERCLASS_GRAPHICSDEVICE ) {

			CString	sMsg;
			sMsg = "Default Graphics Device Driver \"";
			sMsg += sClassName;
			sMsg += "\" could not be found.\n";
			sMsg += "Use Demo Properties to set the new default Graphics Device Driver.";
			::MessageBox( NULL, sMsg, "Moppi Demopaja", MB_OK | MB_ICONWARNING );

			m_rDefaultGraphicsDeviceId = NULL_CLASSID;
		}
	}

	// Default graphics driver has not initialised, use OpenGL driver as default.
	if( m_rDefaultGraphicsDeviceId == NULL_CLASSID )
		m_rDefaultGraphicsDeviceId = CLASS_OPENGL_DEVICEDRIVER;

	// Get correct name for the graphics driver class.
	ClassDescC*	pDesc = m_rFactory.get_classdesc( m_rDefaultGraphicsDeviceId );
	if( pDesc && pDesc->get_classtype() == CLASS_TYPE_DEVICEDRIVER &&
		pDesc->get_super_class_id() == SUPERCLASS_GRAPHICSDEVICE ) {
		m_sDefaultGraphicsDeviceName = pDesc->get_name();
	}


	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views.

	CSingleDocTemplate* pDocTemplate;
	pDocTemplate = new CSingleDocTemplate(
		IDR_MAINFRAME,
		RUNTIME_CLASS(CDemopajaDoc),
		RUNTIME_CLASS(CMainFrame),       // main SDI frame window
		RUNTIME_CLASS(CDemopajaView));

	ASSERT(m_pDocManager == NULL);
	m_pDocManager = new CDocManagerEx;

	AddDocTemplate(pDocTemplate);

	// Enable DDE Execute open
	EnableShellOpen();
	RegisterShellFileTypes( TRUE );

	// Parse command line for standard shell commands, DDE, file open
	CCommandLineInfo cmdInfo;
	ParseCommandLine(cmdInfo);


	// Dispatch commands specified on the command line
	if( !ProcessShellCommand( cmdInfo ) ) {
		TRACE( "ProcessShellCommand failed.\n" );
		return FALSE;
	}

	// The one and only window has been initialized, so show and update it.
	m_pMainWnd->ShowWindow( SW_SHOW );
	m_pMainWnd->UpdateWindow();

	// Enable drag/drop open
	m_pMainWnd->DragAcceptFiles();

	// recalc view
	CDemopajaDoc*	pDoc = GetDoc();
	pDoc->UpdateAllViews( 0, RECALC_VIEWS );


	// update FMOD

	m_i32FMODDriver = GetProfileInt( "FMOD", "Driver", FSOUND_OUTPUT_DSOUND );
	m_i32FMODDevice = GetProfileInt( "FMOD", "Device", 0 );
	m_i32FMODMixer = GetProfileInt( "FMOD", "Mixer", FSOUND_MIXER_QUALITY_AUTODETECT );
	m_i32FMODRate = GetProfileInt( "FMOD", "Rate", 44100 );

	FSOUND_SetOutput( m_i32FMODDriver );
	FSOUND_SetDriver( m_i32FMODDevice );
	FSOUND_SetMixer( m_i32FMODMixer );

	if( !FSOUND_Init( m_i32FMODRate, 32, 0 ) ) {
		CString	sErr;
		sErr.Format( "%s", FMOD_ErrorString( FSOUND_GetError() ) );
		::MessageBox( NULL, sErr, "FMOD Error", MB_OK | MB_ICONERROR );
		m_bFMODPresent = false;
	}
	else {
		m_bFMODPresent = true;
	}


	return TRUE;
}


const char*
CDemopajaApp::GetCurrentDir( int32 i32CurDir )
{
	switch( i32CurDir ) {
	case CURDIR_OPEN: return m_sCurrentDir[0].c_str();
	case CURDIR_SAVE: return m_sCurrentDir[1].c_str();
	case CURDIR_IMPORT: return m_sCurrentDir[2].c_str();
	case CURDIR_EXPORT: return m_sCurrentDir[3].c_str();
	case CURDIR_COLORSWATCH: return m_sCurrentDir[4].c_str();
	}
	return NULL;
}

void
CDemopajaApp::SetCurrentDir( int32 i32CurDir, const char* szOpenDir )
{
	char	szPathBuffer[_MAX_PATH];
	char	szDrive[_MAX_DRIVE];
	char	szDir[_MAX_DIR];
	_splitpath( szOpenDir, szDrive, szDir, NULL, NULL );
	_makepath( szPathBuffer, szDrive, szDir, NULL, NULL );

	switch( i32CurDir ) {
	case CURDIR_OPEN:
		m_sCurrentDir[0] = szPathBuffer;
		break;
	case CURDIR_SAVE:
		m_sCurrentDir[1] = szPathBuffer;
		break;
	case CURDIR_IMPORT:
		m_sCurrentDir[2] = szPathBuffer;
		break;
	case CURDIR_EXPORT:
		m_sCurrentDir[3] = szPathBuffer;
		break;
	case CURDIR_COLORSWATCH:
		m_sCurrentDir[4] = szOpenDir;
		break;
	}
}


void CDemopajaApp::GetMusicSystemConfig( int32& i32Driver, int32& i32Device, int32& i32Mixer, int32& i32Rate )
{
	i32Driver = m_i32FMODDriver;
	i32Device = m_i32FMODDevice;
	i32Mixer = m_i32FMODMixer;
	i32Rate = m_i32FMODRate;
}

void CDemopajaApp::SetMusicSystemConfig( int32 i32Driver, int32 i32Device, int32 i32Mixer, int32 i32Rate )
{
	FSOUND_SetOutput( i32Driver );
	FSOUND_SetDriver( i32Device );
	FSOUND_SetMixer( i32Mixer );

	if( !FSOUND_Init( i32Rate, 32, 0 ) ) {
		CString	sErr;
		sErr.Format( "%s", FMOD_ErrorString( FSOUND_GetError() ) );
		::MessageBox( NULL, sErr, "FMOD Error", MB_OK | MB_ICONERROR );
		m_bFMODPresent = false;
	}
	else {
		m_bFMODPresent = true;
	}

	m_i32FMODDriver = i32Driver;
	m_i32FMODDevice = i32Device;
	m_i32FMODMixer = i32Mixer;
	m_i32FMODRate = i32Rate;

	// save to registry
	WriteProfileInt( "FMOD", "Driver", m_i32FMODDriver );
	WriteProfileInt( "FMOD", "Device", m_i32FMODDevice );
	WriteProfileInt( "FMOD", "Mixer", m_i32FMODMixer );
	WriteProfileInt( "FMOD", "Rate", m_i32FMODRate );
}

void CDemopajaApp::SetMusicDriver( int32 i32Driver )
{
	FSOUND_SetOutput( i32Driver );
}

int32 CDemopajaApp::GetMusicDeviceCount()
{
	return FSOUND_GetNumDrivers();
}

const char* CDemopajaApp::GetMusicDeviceName( int32 i32Index )
{
	return (const char*)FSOUND_GetDriverName( i32Index );
}


bool CDemopajaApp::MusicSystemPresent()
{
	return m_bFMODPresent;
}

const char* CDemopajaApp::GetPluginDir()
{
	return m_sPluginPath.c_str();
}

const char* CDemopajaApp::GetAppDir()
{
	return m_sAppPath.c_str();
}

int CDemopajaApp::ExitInstance() 
{
	if( m_bFMODPresent )
		FSOUND_Close();

	// Store default graphics driver
	WriteProfileString( "Settings", "DefaultGraphicsDeviceName", m_sDefaultGraphicsDeviceName.c_str() );
	CString	sClassId;
	sClassId.Format( "0x%08x 0x%08x", m_rDefaultGraphicsDeviceId.get_class_a(), m_rDefaultGraphicsDeviceId.get_class_b() );
	WriteProfileString( "Settings", "DefaultGraphicsDeviceID", sClassId );

	// Current dir
	WriteProfileString( "Settings", "CurrentDirOpen", m_sCurrentDir[0].c_str() );
	WriteProfileString( "Settings", "CurrentDirSave", m_sCurrentDir[1].c_str() );
	WriteProfileString( "Settings", "CurrentDirImp", m_sCurrentDir[2].c_str() );
	WriteProfileString( "Settings", "CurrentDirExp", m_sCurrentDir[3].c_str() );
	WriteProfileString( "Settings", "CurrentDirColorSwatch", m_sCurrentDir[4].c_str() );

	return CWinApp::ExitInstance();
}



int32
get_time()
{
	LARGE_INTEGER	d;
	double			i, j;
	QueryPerformanceCounter( &d );
	i = (double)d.QuadPart;
	QueryPerformanceFrequency( &d );
	j = (double)d.QuadPart;
	return (int32)((i / j) * 1000.0);
}


GraphicsDeviceI* CDemopajaApp::CreateGraphicsDevice()
{
	return (GraphicsDeviceI*)m_rFactory.create( m_rDefaultGraphicsDeviceId );
}

const ClassIdC& CDemopajaApp::GetDefaultGraphicsDeviceId() const
{
	return m_rDefaultGraphicsDeviceId;
}

void CDemopajaApp::SetDefaultGraphicsDeviceId( const PluginClass::ClassIdC& rClassId )
{
	m_rDefaultGraphicsDeviceId = rClassId;
}

const char* CDemopajaApp::GetDefaultGraphicsDeviceName() const
{
	return m_sDefaultGraphicsDeviceName.c_str();
}

void CDemopajaApp::SetDefaultGraphicsDeviceName( const char* szName )
{
	m_sDefaultGraphicsDeviceName = szName;
}


void CDemopajaApp::PlayPreview( int32 i32Time, int32 i32MaxTime, bool bPlayOnce )
{
	CDemopajaDoc*	pDoc = GetDoc();
	if( !pDoc )
		return;

	SceneC*			pScene = pDoc->GetCurrentScene();
	DeviceContextC*	pDeviceContext = pDoc->GetDeviceContext();
	int32			i32FrameSizeInTicks = pDoc->GetFrameSizeInTicks();
	int32			i32LayoutWidth = pDoc->GetLayoutWidth();
	int32			i32LayoutHeight = pDoc->GetLayoutHeight();

	//
	// Get current graphics device
	//
	GraphicsDeviceI*	pDevice = (GraphicsDeviceI*)pDeviceContext->query_interface( SUPERCLASS_GRAPHICSDEVICE );

	if( !pDevice ) {
		TRACE( "ERROR: Could not get graphics device from view.\n" );
		return;
	}

	// Get descktop BPP
	HDC dc = ::GetDC( 0 ); // desktop dc
	int32	i32BPP = ::GetDeviceCaps( dc, BITSPIXEL );
	ReleaseDC( 0, dc );

	if( !pDevice->set_fullscreen( i32LayoutWidth, i32LayoutHeight, i32BPP ) ) {
		::MessageBox( NULL, "Could not set the graphics device to fullscreen.\r\nCannot preview.", "Error!", MB_OK );
		return;
	}

	TimeContextC	rTimeContext( pDoc->GetBeatsPerMin(), pDoc->GetQNotesPerBeat(), pDoc->GetEditAccuracy() );
	int32			i32RenderTime = i32Time;
	int32			i32MaxRenderTime = i32MaxTime;
	float64			f64TimeScale = (float64)pDoc->GetBeatsPerMin() * (float64)pDoc->GetQNotesPerBeat() * 256.0 / 60.0;
	uint32			ui32FrameID = 0;

	std::vector<Composition::EffectI*>	rEffectStack;
	std::vector<PajaTypes::int32>				rEffectTimeStack;

	// hide cursor
	while( ::ShowCursor( FALSE ) > 0 ) {};

	// Start music
	pDoc->PlayMusic( i32Time );

//	int32	i32StartTime = get_time() - (int32)((float64)i32RenderTime * 1000.0 / f64TimeScale);
	MSG		rMsg;
	int32	i, j, k;

	do {
		if( PeekMessage( &rMsg , pDevice->get_hwnd() , 0 , 0 , PM_REMOVE ) ) {
			TranslateMessage( &rMsg ) ;
			DispatchMessage( &rMsg );
		}
		else {
//			int32	i32DiffTime = get_time() - i32StartTime;
//			i32RenderTime = (int32)((float64)i32DiffTime / 1000.0 * f64TimeScale); // * (float64)i32FrameSizeInTicks);
			i32RenderTime = pDoc->GetMusicPos(); //(int32)((float64)pDoc->GetMusicPos() / 1000.0 * f64TimeScale);
			if( i32RenderTime > i32MaxRenderTime ) {
				if( bPlayOnce )
					break;
				else {
//					i32StartTime = get_time();
					pDoc->SetPosMusic( 0 );
					i32RenderTime = 0;
				}
			}

			rTimeContext.set_frame_id( ui32FrameID );
			ui32FrameID++;

			// set the device active
			pDevice->activate();

			// clear device
			pDevice->clear_device( GRAPHICSDEVICE_ALLBUFFERS, pScene->get_layout_color() );

			if( !pDevice->begin_draw() )
				continue;

			//
			// draw effects
			//

			pDevice->begin_effects();

			for( i = pScene->get_layer_count() - 1; i >= 0 ; i-- ) {

				LayerC*	pLayer = pScene->get_layer( i );

				if( !pLayer )
					continue;

				if( !pLayer->get_timesegment()->is_visible( i32RenderTime ) )
					continue;

				// Make sure there's enough space for all the layers in the stack.
				if( rEffectStack.empty() || rEffectStack.size() < pLayer->get_effect_count() )
				{
					rEffectStack.resize( pLayer->get_effect_count() + 1 );
					rEffectTimeStack.resize( pLayer->get_effect_count() + 1 );
				}

				// Init stack
				for( j = 0; j < (int32)rEffectStack.size(); j++ )
				{
					rEffectStack[j] = 0;
					rEffectTimeStack[j] = 0;
				}

				int32	i32StackHead = 0;

				int32	i32TimeOffset;
				i32TimeOffset = pLayer->get_timesegment()->get_segment_start();

				for( int32 j = pLayer->get_effect_count() - 1; j >= 0; j-- ) {
					EffectI*	pEffect = pLayer->get_effect( j );

					if( !pEffect )
						continue;

					if( pEffect->get_timesegment()->is_visible( i32RenderTime - i32TimeOffset ) )
					{
						if( pEffect->get_super_class_id() == SUPERCLASS_EFFECT_POST_PROCESS )
						{
							EffectPostProcessI*	pPostProcess = (EffectPostProcessI*)pEffect;

							// use render target instead
							GraphicsBufferI*	pOldGBuffer = 0;
							GraphicsBufferI*	pGBuffer = pPostProcess->get_render_target();

							if( !pGBuffer )
								continue;

							pOldGBuffer = pDevice->set_render_target( pGBuffer );

							if( pGBuffer->get_state() == DEVICE_STATE_LOST )
							{
								pDevice->set_render_target( pOldGBuffer );
								continue;
							}

							GraphicsViewportI*	pBufViewport = (GraphicsViewportI*)pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
							if( !pBufViewport )
							{
								pDevice->set_render_target( pOldGBuffer );
								continue;
							}

							// Save current layout and viewport
							BBox2C	rOldLayout = pBufViewport->get_layout();
							BBox2C	rOldViewport = pBufViewport->get_viewport();
							float32	f32OldAspect = pBufViewport->get_pixel_aspect_ratio();

							// Set new based on the scene effects bounding box
							BBox2C	rNewLayout( Vector2C( 0, 0 ), Vector2C( (float32)pScene->get_layout_width(), (float32)pScene->get_layout_height() ) );
							BBox2C	rNewViewport( Vector2C( 0, 0 ), Vector2C( (float32)pScene->get_layout_width(), (float32)pScene->get_layout_height() ) );
							BBox2C	rOldClipLayout;


							// set newly calculated layout viewport
							// the viewport is set after begin_draw() because begin draw saves the viewport and projection matrices.
							pBufViewport->set_layout( rNewLayout );
							pBufViewport->set_viewport( rNewViewport );

							// Set correct pixels size.
							// It's often that the graphics buffer has different aspect ratio than the rendering
							// output is. Problems arise on perspective projection etc. The following code
							// handles the situation.
							float32	f32AspectVP = (float32)pBufViewport->get_height() / (float32)pBufViewport->get_width();
							float32	f32AspectHW = (float32)pScene->get_layout_height() / (float32)pScene->get_layout_width();
							pBufViewport->set_pixel_aspect_ratio( f32AspectHW / f32AspectVP  );


							// clear device
							ColorC	BGCol = pScene->get_layout_color();
							BGCol[3] = 0.0f;
							pDevice->clear_device( GRAPHICSDEVICE_ALLBUFFERS, BGCol );

							pDevice->begin_draw();

							//
							// draw effects
							//

							pDevice->begin_effects();

							// Render all effects in the stack into the effect's render target.
							for( k = 0; k < i32StackHead; k++ )
							{
								if( rEffectStack[k] )
								{
									EffectI*	pFX = rEffectStack[k];
									pFX->eval_state( rEffectTimeStack[k] );
								}
							}

							// restore old layout and viewport
							pBufViewport->set_layout( rOldLayout );
							pBufViewport->set_viewport( rOldViewport );
							pBufViewport->set_pixel_aspect_ratio( f32OldAspect );

							pDevice->end_effects();
							pDevice->end_draw();

							// flush device (swap buffers)
							pDevice->flush();

							pDevice->set_render_target( pOldGBuffer );

							// Reset stack.
							i32StackHead = 0;
						}

						rEffectStack[i32StackHead] = pEffect;
						rEffectTimeStack[i32StackHead] = i32RenderTime - (i32TimeOffset + pEffect->get_timesegment()->get_segment_start());
						i32StackHead++;

//						pEffect->eval_state( i32RenderTime - (i32TimeOffset + pEffect->get_timesegment()->get_segment_start()) );
					}
				}

				// Flush the stack.
				for( k = 0; k < i32StackHead; k++ )
				{
					if( rEffectStack[k] )
					{
						EffectI*	pFX = rEffectStack[k];
						pFX->eval_state( rEffectTimeStack[k] );
					}
				}
			}


			pDevice->end_effects();

			pDevice->end_draw();

			// flush device (swap buffers)
			pDevice->flush();
		}
	} while( rMsg.message != DP_END_PREVIEW );

	// Stop music
	pDoc->StopMusic();

	// show cursor
	while( ShowCursor( TRUE ) < 0 ) {};

	pDevice->set_windowed();
}



/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	CStatic	m_rStaticCredits;
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	afx_msg void OnPic();
	afx_msg void OnPaint();
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	DDX_Control(pDX, IDC_CREDITS, m_rStaticCredits);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
	ON_BN_CLICKED(IDC_PIC, OnPic)
	ON_WM_PAINT()
	ON_WM_CTLCOLOR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CDemopajaApp::OnAppAbout()
{
	CAboutDlg aboutDlg;
	aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CDemopajaApp message handlers


void CAboutDlg::OnPic() 
{
	EndDialog( IDOK );
}

void CAboutDlg::OnPaint() 
{
	CPaintDC dc(this);
	CRect	rRect;
	m_rStaticCredits.GetWindowRect( rRect );
	ScreenToClient( rRect );

	CFont	rFont;
	rFont.CreateFont( 12, 0, 0, 0, FW_NORMAL, 0, 0, 0,
		ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		ANTIALIASED_QUALITY,
		DEFAULT_PITCH | FF_DONTCARE,
		"Arial" );

	CFont*	pOldFont = dc.SelectObject( &rFont );

	dc.SetBkMode( TRANSPARENT );
	dc.SetTextColor( RGB( 196, 196, 196 ) );

	int32	i32Build = 2;

#ifdef _DEBUG
	CString	sStr;
	sStr.Format( "Version %d.%d%d DEBUG   Copyright (c) 2000 - 2002 Moppi Productions\nhttp://www.moppiproductions.net\n", DEMOPAJA_VER_MAJOR, DEMOPAJA_VER_MINOR, i32Build );
#else
	CString	sStr;
	sStr.Format( "Version %d.%d%d   Copyright (c) 2000 - 2002 Moppi Productions\nhttp://www.moppiproductions.net\n", DEMOPAJA_VER_MAJOR, DEMOPAJA_VER_MINOR, i32Build );
#endif

	sStr += "Uses FlatPopupMenu by Andy Brown <andy@mirage.dabsol.co.uk> and\n";
	sStr += "Sizing Control Bar by Cristi Posea <www.datamekanix.com>\n";
	sStr += "CButtonST by Davide Calabro <davide_calabro@yahoo.com>";

	dc.DrawText( sStr, rRect, DT_CENTER );

	dc.SelectObject( pOldFont );
}

HBRUSH CAboutDlg::OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor )
{
	HBRUSH hbr = CDialog::OnCtlColor( pDC, pWnd, nCtlColor );
	
	if( nCtlColor == CTLCOLOR_DLG ) {
		return (HBRUSH)::GetStockObject( BLACK_BRUSH );
	}

	return hbr;
}

