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

#include "stdafx.h"
#include "Pop.h"

#include "ChildFrm.h"
#include "critter.h" //So you know how to delete a critter_clipboard.
#include "MainFrm.h"
#include <mmsystem.h> //for PlaySound
#include "PopDoc.h"
#include "PopView.h"
#include "dialogspeed.h" //for cSpeedDialog
#include "dialogopenglinfo.h" //for cDialogOpenGLInfo

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

//================== CPopApp Statics =============================
Real CPopApp::MINDT = 0.01; //Shortest allowable time step in seconds, means 100 updates a second.
int CPopApp::STARTPIXELWIDTH = 800;
int CPopApp::STARTPIXELHEIGHT = 600;
int CPopApp::MAXCHILDWINDOWCOUNT = 4; /* Don't allow more than this many child views open. */
/////////////////////////////////////////////////////////////////////////////
// CPopApp

BEGIN_MESSAGE_MAP(CPopApp, CWinApp)
	//{{AFX_MSG_MAP(CPopApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_GAME_PLAYSOUNDS, OnGamePlaysounds)
	ON_UPDATE_COMMAND_UI(ID_GAME_PLAYSOUNDS, OnUpdateGamePlaysounds)
	ON_COMMAND(ID_FILE_NEW, OnFileNew)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
	ON_COMMAND(ID_GAME_SPEED, OnGameSpeed)
	ON_UPDATE_COMMAND_UI(ID_GAME_ANIMATE, OnUpdateGameAnimate)
	ON_COMMAND(ID_GAME_ANIMATE, OnGameAnimate)
	ON_COMMAND(ID_OPENGLINFODIALOG, OnOpenglinfodialog)
	//}}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()

/////////////////////////////////////////////////////////////////////////////
// CPopApp construction

CPopApp::CPopApp():
_soundflag(TRUE), //Start with sound turned on
_runspeed(1.0), //Start with realtime runspeed
_animateflag(TRUE), //Start with animation off for framework, make TRUE for apps.
_pcritter_clipboard(NULL)
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
	_timer.setMinDt(CPopApp::MINDT); //Don't run faster than 100 updates a second.
	/* The default setting of the _timer's MinDt gets overriden in the mainframe.cpp
	file where we try and set the mindt so as not to outstrip the graphics card 
	update speed: there's no point doing updates you can't see. We can't do this here
	because we need a valid HWND in order to get an HDC to make a GetDeviceCaps call
	to find out the refresh rate. */
}

CPopApp::~CPopApp()
{
	delete _pcritter_clipboard;
	_pcritter_clipboard = NULL;
	cRandomizer::deleteSingleton(); /* Need this or you have a memory leak. */
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CPopApp object

CPopApp theApp;

/////////////////////////////////////////////////////////////////////////////
// CPopApp initialization

BOOL CPopApp::InitInstance()
{
// Resource Initialization code by RR.===================
	/* Any code which loads resources for the views to use has to go in here
	before the ParseCommandLine code below, because the view is going to be
	created in ParseCommandLine and you have to get ready first. 
	Note that the resource-loading code can't go into the overloaded CWinApp
	constructor because the App isn't "alive" enough yet at that point to
	be able to load it's resrouces. */
	_hCursorPlay = LoadStandardCursor(IDC_CROSS);
	_hCursorArrow = LoadStandardCursor(IDC_ARROW);
	_hCursorDragger = LoadCursor(IDC_DRAGHAND);
	_hCursorPin = LoadCursor(IDC_PIN);
	_hCursorZap = LoadCursor(IDC_ZAP);
	_hCursorReplicate = LoadCursor(IDC_REPLICATE);
	_hCursorSpawn = LoadCursor(IDC_SPAWN);

//End Resource Initialization code by RR.====================
//=======App Wizard code from here on.============
	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.

/* We don't need the Enable3dControls calls anymore, these are deprecated now and in 
fact they throw a warning in Visual Studio 7, and they don't make a difference in
Visual Studio 6.
#ifdef _AFXDLL
	Enable3dControls();			// Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();	// Call this when linking to MFC statically
#endif
*/

	// Change the registry key under which our settings are stored.
	// TODO: You should modify this string to be something appropriate
	// such as the name of your company or organization.
	SetRegistryKey(_T("Local AppWizard-Generated Applications"));
	LoadStdProfileSettings();  // Load standard INI file options (including MRU)

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

	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(
		IDR_POPTYPE,
		RUNTIME_CLASS(CPopDoc),
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame
		RUNTIME_CLASS(CPopView)); //The pressure point where we pick what kind of default view to use.
	AddDocTemplate(pDocTemplate);

	// create main MDI Frame window
	CMainFrame* pMainFrame = new CMainFrame;
	if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
		return FALSE;
	m_pMainWnd = pMainFrame;

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

	// Dispatch commands specified on the command line
	if (!ProcessShellCommand(cmdInfo))
		return FALSE;

	// The main window has been initialized, so show and update it.
	//pMainFrame->ShowWindow(SW_SHOWMAXIMIZED | m_nCmdShow); //Use this version if you want main window maximized.
	pMainFrame->ShowWindow(m_nCmdShow); //Use this version if you don't want main window maximized.
	pMainFrame->UpdateWindow();

	return TRUE;
}

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

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

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}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)
		// No message handlers
	//}}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)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

//////////////////////////////////////////////////////////////////////////
//cOpenGLDialog


/////////////////////////////////////////////////////////////////////////////
// CPopApp message handlers


void CPopApp::OnFileNew() 
{
	CMainFrame* mainframe = (CMainFrame*)::AfxGetMainWnd();
	if (mainframe->getMDIChildCount() >= CPopApp::MAXCHILDWINDOWCOUNT)
	{
		::MessageBeep(MB_ICONEXCLAMATION);
		return;
	}
	CWinApp::OnFileNew(); //Call base class handler first.
	//-------My Code.  RR. See comment on CMainFrame::OnWindowNew
	// Purpose of this is to glue multiple windows into tiled position.
	if (mainframe->_autotile && mainframe->getMDIChildCount() > 1) //Automatically keep all views tiled
		mainframe->MDITile(MDITILE_VERTICAL);
}

void CPopApp::OnFileOpen() 
{
	CMainFrame* mainframe = (CMainFrame*)::AfxGetMainWnd();
	if (mainframe->getMDIChildCount() >= CPopApp::MAXCHILDWINDOWCOUNT)
	{
		::MessageBeep(MB_ICONEXCLAMATION);
		return;
	}
	CWinApp::OnFileOpen(); /* This pops up a dialog box to get the
		lpszFileName, then calls OpenDocumentFile(LPCTSTR lpszFileName) */
	//-------My Code.  RR. See comment on CMainFrame::OnWindowNew
	// Purpose of this is to glue multiple windows into tiled position.
	if (mainframe->_autotile && mainframe->getMDIChildCount() > 1) //Automatically keep all views tiled
		mainframe->MDITile(MDITILE_VERTICAL);
}

CDocument* CPopApp::OpenDocumentFile(LPCTSTR lpszFileName)
{
	ASSERT(m_pDocManager != NULL); //Base class code
//Special Pop framework code starts here.
//First of all we clean up the 
	CString filename(lpszFileName);
	filename.MakeLower();
	int lastslashindex = filename.ReverseFind('\\'); 
		 /* Finds the index of the last backslash in the string, as is used in
		a dirctory name like C:\pop\test.pop". As you start counting with 0, you'd
		get 6 in this case. Note that I have to use the escape sequence '\\' as '\' won't compile. */
	if (lastslashindex != -1) //ReverseFind returns a -1 if no slash is found in the file name.  
		/* Note that if we didn't find a slash, we will use the whole filenname, so there's nothing
		to truncate, so we don't do this next step. */
		filename = filename.Right(filename.GetLength()-(lastslashindex+1)); 
			/* Right(n) returns the last n characters.  As the slash is at lastindex, there are
			lastindex+1 chars we don't want. */
	CPopDoc *pdoc = getDocByTitle(filename);
	if (pdoc)
		pdoc->OnCloseDocument(); //Close doc with same name.
//Special Pop framework code ends here.
	return m_pDocManager->OpenDocumentFile(lpszFileName); //Base class code
}


void CPopApp::OnGameSpeed() 
{
/* I want to have the user set _runspeed by setting an index from 1 to 10.
  Rather than having a linear scale, we'll list a specific runspeed value for
	each smoothnessIndex choice. In the interface we speak of higher smoothnessIndex
	as meaning smoother, slower motion, so we ramp the runspeed values the opposite
	way from the indices. */

	static double smoothnessIndexToRunspeed[10] =
		{10.0, 7.0, 5.0, 3.0, 2.0, 1.0, 0.7, 0.4, 0.1, 0.01}; 
		//  0	 1    2    3    4	 5    6    7    8     9	
	static int runspeedindex = 4;
	int nUpdatesPerSecond = int(_timer.updatesPerSecond());
	cSpeedDialog dlg;
	if (!nUpdatesPerSecond)
		dlg.m_cstrUpdatespersecond.Format("Less than one.");
	else	
		dlg.m_cstrUpdatespersecond.Format("%d", nUpdatesPerSecond);
	dlg.m_nSpeed = runspeedindex + 1;
	/* When using a modal dialog, it's important to bail out and not use its
		dlg values in the case where you exit the dialog by a Cancel, because
		then these will be bad values.  In other words, if I just call
		dlg.DoModal() instead of the next line, then if I input a bad value
		like -1 for the speed, and then Cancel out of the dialog, the bad
		value is still in dlg.m_nSpeed, and would get (disatrously) used.  So
		that's why I need to not use the values when DoModal returns IDCANCEL. */
	if (dlg.DoModal() == IDCANCEL)
		return;
	runspeedindex = dlg.m_nSpeed - 1; //Will lie between 1 and 10
	_runspeed = smoothnessIndexToRunspeed[runspeedindex];
}

BOOL CPopApp::OnIdle(LONG lCount) 
{
/* The default CWinApp behavior is to return CWinApp::OnIdle(lCount).
We'll do exactly this in the cases where either _animateflag is false or
where my WinApp is not the active window currently being used.
I do this second case so the program won't soak a lot of CPU time
when it's not in the foreground. If I really wanted the program to 
run off to one side like a miniatue screensaver, I could comment these
three lines our, alternately, I could put a switch in the CMainFrame to turn
them or and off.*/
//============ Non-foreground mode ======================
	CWnd *cwmainframe = AfxGetMainWnd();
	if (cwmainframe != cwmainframe->GetForegroundWindow()) 
		return CWinApp::OnIdle(lCount); 
		 
//============ Foreground mode ==========================
/*In the case where my WinApp is active, I'll first do the baseclass
processing, and then I'll start animating my documents. And then I'll return TRUE, 
which means that I want the WinApp::Run to keep on calling this OnIdle function 
over and over.
The lCount param tells you how many times OnIdle has been called since the last
message was processed.  Windows uses the  0th and 1st call to OnIdle for its own 
stuff, so we don't animate until those have been done, otherwise Windows has to
wait after the 0th for the 1st call, and this could cause GUI lag. */
	CWinApp::OnIdle(lCount); //Do the base class WinApp processing.
	if (lCount < 2) //Still doing Windows OnIdle processing, so call OnIdle again.
		return TRUE;
	/* Even if you're not animating, any change, such as a key press or a mouse 
action will generate a call to OnIdle, and we use this as chance to keep the docs
up to date. */
	if (!_animateflag)
	{ // Do a single update with a dt of 0.0.
		animateAllDocs(0.0);
		return FALSE; //Don't need to call OnIdle again until a message is received.
	}
	// The lCount >= 2 and _animateflag case.
	Real dt = _runspeed*_timer.tick(); //Update the dt cycle time.
	animateAllDocs(dt); //Step through all the docs and animate each doc and its views.
	return TRUE; //Keep doing OnIdle over and over while waiting for another message.
}

/////////////////////////////////////////////////////////////////////////////
// My CPopApp functions

void CPopApp::playSound(LPCSTR pwavfileresourcename,  DWORD soundflags)
{
	if (_soundflag)
		::PlaySound(pwavfileresourcename, NULL, soundflags);   
}

void CPopApp::animateAllDocs(Real dt)
{
	CMultiDocTemplate* pSelectedTemplate;
	CPopDoc* pDoc;
	POSITION docpos;
	POSITION pos = GetFirstDocTemplatePosition();
	if (pos == NULL)
		return; //No doc template exists yet.
	pSelectedTemplate = (CMultiDocTemplate*)GetNextDocTemplate(pos);
	docpos = pSelectedTemplate->GetFirstDocPosition();//find first document
	while(docpos != NULL) //while there are documents left to process
	{
		pDoc = (CPopDoc*)pSelectedTemplate->GetNextDoc(docpos); /* This retrieves
			the document at position docpos and then increments docpos to the next
			position. */
		pDoc->stepDoc(dt);  /* CAlifeDocument::stepDoc() will animate the *pDoc and
			call UpdateAllViews to update the *pDoc's views. */
	}
}

CPopDoc* CPopApp::getActiveDoc()
{
	CMultiDocTemplate* pSelectedTemplate;
	CPopDoc* pDoc;
	POSITION docpos;
	POSITION pos = GetFirstDocTemplatePosition();
	if (pos == NULL)
		return NULL; //No doc template exists yet.
	pSelectedTemplate = (CMultiDocTemplate*)GetNextDocTemplate(pos);
	docpos = pSelectedTemplate->GetFirstDocPosition();//find first document
	while(docpos != NULL) //while there are documents left to process
	{
		pDoc = (CPopDoc*)pSelectedTemplate->GetNextDoc(docpos); /* This retrieves
			the document at position docpos and then increments docpos to the next
			position. */
		if (pDoc->getActiveView() && pDoc->getActiveView()->GetDocument()==pDoc)
			return pDoc;
	}
	return NULL;
}

CPopDoc* CPopApp::getDocByTitle(CString targettitle)
{
	CMultiDocTemplate* pSelectedTemplate;
	CPopDoc* pDoc;
	POSITION docpos;
	POSITION pos = GetFirstDocTemplatePosition();
	if (pos == NULL)
		return NULL; //No doc template exists yet.
	pSelectedTemplate = (CMultiDocTemplate*)GetNextDocTemplate(pos);
	docpos = pSelectedTemplate->GetFirstDocPosition();//find first document
	while(docpos != NULL) //while there are documents left to process
	{
		pDoc = (CPopDoc*)pSelectedTemplate->GetNextDoc(docpos); /* This retrieves
			the document at position docpos and then increments docpos to the next
			position. */
		CString doctitle = pDoc->GetTitle();
			 //The title is the name+file extension without any directory info.
		targettitle.MakeLower(); //To avoid false negatives, we discard case info.
		doctitle.MakeLower();
		if (doctitle == targettitle)
			return pDoc;
	}
	return NULL; //If no doc with this title is found.
}

BOOL CPopApp::isDocOpen()
{
	CMultiDocTemplate* pSelectedTemplate;
	POSITION docpos;
	POSITION pos = GetFirstDocTemplatePosition();
	if (pos == NULL)
		return FALSE; //No doc template exists yet.
	pSelectedTemplate = (CMultiDocTemplate*)GetNextDocTemplate(pos);
	docpos = pSelectedTemplate->GetFirstDocPosition();//find first document
	if (docpos == NULL) //no documents  to process
		return FALSE;
	else
		return TRUE;
}

void CPopApp::OnGamePlaysounds() 
{
	_soundflag ^= TRUE;	
}

void CPopApp::OnUpdateGamePlaysounds(CCmdUI* pCmdUI) 
{ //Labelled as "Mute" so show as !_soundflag.
	pCmdUI->Enable(isDocOpen()?TRUE:FALSE);	
	pCmdUI->SetCheck(_soundflag?0:1);	
}


void CPopApp::OnUpdateGameAnimate(CCmdUI* pCmdUI) 
{ //This is labeled "Pause" now, so we check as !_animateflag.
	pCmdUI->Enable(isDocOpen()?TRUE:FALSE);	
	pCmdUI->SetCheck(_animateflag?0:1);	
}

void CPopApp::OnGameAnimate() 
{
	_animateflag ^= TRUE;	
	if (_animateflag)
		_timer.reset(); /* If you are just turning on the animateflag, you need
		to reset the cycle counter */
}

void CPopApp::OnOpenglinfodialog() 
{
	cDialogOpenGLInfo infodlg;
	infodlg.DoModal();
}

int CPopApp::cursorToID(HCURSOR hcursor)
{
	if (hcursor == _hCursorPlay)
		return 0;
	if (hcursor == _hCursorArrow)
		return 1;
	if (hcursor == _hCursorDragger)
		return 2;
	if (hcursor == _hCursorPin)
		return 3;
	if (hcursor == _hCursorZap)
		return 4;
	if (hcursor == _hCursorReplicate)
		return 5;
	if (hcursor == _hCursorSpawn)
		return 6;
	else
		return 1;
}

HCURSOR CPopApp::IDToCursor(int cursorid)
{
	if (cursorid == 0)	
		return _hCursorPlay;
	if (cursorid == 1)	
		return _hCursorArrow;
	if (cursorid == 2)	
		return _hCursorDragger;
	if (cursorid == 3)	
		return _hCursorPin; 
	if (cursorid == 4)	
		return _hCursorZap;
	if (cursorid == 5)	
		return _hCursorReplicate;
	if (cursorid == 6)	
		return _hCursorSpawn;
	else
		return _hCursorArrow;
}
