/*
Copyright (c) 1998 Richard Lawrence

This program is free software; you can redistribute it and/or modify it under the terms 
of the GNU General Public License as published by the Free Software Foundation; either 
version 2 of the License, or (at your option) any later version. This program is 
distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details. You should have received a copy of the GNU
General Public License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// Atari800WinView.cpp : implementation of the CAtari800WinView class
//

#include "stdafx.h"
#include "Atari800Win.h"

#include "Atari800WinDoc.h"
#include "Atari800WinView.h"
#include "CErrorLogDlg.h"
#include "winatari.h"
#include "mmsystem.h"
#include "ddraw.h"
#include "graphics.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

extern "C" {
#include "atari.h"
#include "log.h"
#include "registry.h"
extern ULONG	ulAtari_HW_Nexttime;
extern LARGE_INTEGER	lnTimeFreq;
extern ULONG ulDeltaT;
extern int	nStatusSize;
extern DWORD WINAPI		StartAtariThread( LPVOID );
extern HWND				hWnd, MainhWnd;
extern HDC				h_screenDC;
extern int		Atari_Exit( int );
extern ULONG	ulAtariState;
extern ULONG	ulMiscStates;
extern ULONG	*atari_screen;
extern BOOL		bTimerRollover;
extern int		Sound_Initialise( void );
extern ULONG ulScreenMode;
extern int Init_Graphics( void );
extern HINSTANCE hInstance;
extern LPBITMAPINFO	lpbmi;		/* bitmap info for our screen bitmap */
extern void Clear_Sound( BOOL bPermanent );
extern void Restart_Sound( void );
extern void DescribeAtariSystem( void );
extern int Input_Initialise( void );
extern int	enable_sio_patch;
extern void Reset_Keys( void );
extern void SafeShowScreen( void );
}

#ifdef ATARI_UI
extern void	TerminateUIThread( void );
#endif
static ULONG	unlosttime;
/////////////////////////////////////////////////////////////////////////////
// CAtari800WinView

IMPLEMENT_DYNCREATE(CAtari800WinView, CView)

BEGIN_MESSAGE_MAP(CAtari800WinView, CView)
	//{{AFX_MSG_MAP(CAtari800WinView)
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAtari800WinView construction/destruction

CAtari800WinView::CAtari800WinView()
{
	// TODO: add construction code here
}

CAtari800WinView::~CAtari800WinView()
{
}

/////////////////////////////////////////////////////////////////////////////
// CAtari800WinView drawing

void CAtari800WinView::OnDraw(CDC* pDC)
{
//	CAtari800WinDoc* pDoc = GetDocument();
//	ASSERT_VALID(pDoc);

	if( ulAtariState & ATARI_LOAD_FAILED )
	{
		RECT	rc;
		if( ulAtariState & ATARI_LOAD_WARNING )
		{
			char	szTemp[256];
			char	szFailed[512 + LOADSTRING_STRING_SIZE];
			int		result;

			LoadString( NULL, IDS_BOOT_ERROR, szFailed, LOADSTRING_STRING_SIZE );
			LoadString( NULL, IDS_GENERIC_ERROR, szTemp, 256 );
			result = MessageBox( szFailed, szTemp, MB_ICONWARNING | MB_YESNO );
			ulAtariState &= ~ATARI_LOAD_WARNING;
			if( result == IDYES )
			{
				CErrorLogDlg	ErrorLog;
				ErrorLog.DoModal();
			}
		}

		GetClientRect( &rc );
		pDC->PatBlt( rc.left, rc.top, rc.right, rc.bottom, BLACKNESS );
		return;
	}

	if( !hWnd && !(ulAtariState & ATARI_CLOSING))
	{
		StartAtariMachine( );
		return;
	}

	if( (ulAtariState & (ATARI_PAUSED|ATARI_NOFOCUS)) && !(ulAtariState & ATARI_CLOSING))
	{
		if( atari_screen )
		{
			int		nWidth, nHeight;

			if( ulScreenMode & DDRAW_FULL )
			{
				//SafeShowScreen();		
				return;
			}

			nWidth = lpbmi->bmiHeader.biWidth;
			nHeight = lpbmi->bmiHeader.biHeight;
			lpbmi->bmiHeader.biWidth = ATARI_WIDTH;
			lpbmi->bmiHeader.biHeight = -ATARI_HEIGHT;
			if( ulScreenMode & WINDOWED_STRETCH )
			{
				StretchDIBits( pDC->m_hDC, 0, 0, ATARI_STRETCH_VIS_WIDTH, ATARI_STRETCH_HEIGHT,
					ATARI_HORZ_CLIP, 0, ATARI_VIS_WIDTH, ATARI_HEIGHT,
					(UBYTE *)atari_screen, lpbmi, DIB_RGB_COLORS, SRCCOPY);
			}
			else
			{
				StretchDIBits( pDC->m_hDC, 0, 0, ATARI_VIS_WIDTH , ATARI_HEIGHT,
						ATARI_HORZ_CLIP, 0, ATARI_VIS_WIDTH, ATARI_HEIGHT,
						(UBYTE *)atari_screen, lpbmi, DIB_RGB_COLORS, SRCCOPY );
			}
			lpbmi->bmiHeader.biWidth = nWidth;
			lpbmi->bmiHeader.biHeight = nHeight;
		}
		else
		{
			RECT	rc;

			GetClientRect( &rc );
			pDC->PatBlt( rc.left, rc.top, rc.right, rc.bottom, BLACKNESS );
		}

		return;
	}

	if( ulAtariState & ATARI_CRASHED )
	{
		RECT	rc;
		GetClientRect( &rc );
		pDC->PatBlt( rc.left, rc.top, rc.right, rc.bottom, BLACKNESS );
	}
}

void CAtari800WinView::StartAtariMachine( )
{
	hWnd = GetSafeHwnd();
	h_screenDC = ::GetDC( hWnd );
	
	MainhWnd = AfxGetMainWnd()->GetSafeHwnd();
	hInstance = AfxGetInstanceHandle( );

	Input_Initialise( );

	if( Sound_Initialise() )
	{
		if( !Init_Graphics() )
		{
			ulAtariState |= ATARI_LOAD_FAILED | ATARI_PAUSED;
			Invalidate();
		}
		else
		{
			::InvalidateRect( MainhWnd, NULL, FALSE );
			ulAtariState &= ~ATARI_PAUSED;
			/* Right before we actually start the Atari, we write out a boot failure message
			   to the registry. During _normal_ closing of windows we clear the "failure" mark
			   in the WM_CLOSE handler. This way if the Atari causes the Windows program to
			   crash it's easy to tell by testing for boot failure upon reading the value from
			   the registry initially*/
			ulMiscStates |= ATARI_LAST_BOOT_FAILED;
			WriteRegDWORD( NULL, REG_MISC_STATES, ulMiscStates );
			ulAtariState |= ATARI_WINDOWS_READY | ATARI_RUNNING;
			ulAtariState &= ~ATARI_LOAD_FAILED;
		}
	}
	else
		ulAtariState |= ATARI_LOAD_FAILED | ATARI_PAUSED;

	DescribeAtariSystem( );

#ifdef THREADED
	hAtariWorkThread = CreateThread( NULL, 0,(LPTHREAD_START_ROUTINE) StartAtariThread,&dwThrdParam,0,&dwThreadId);
#endif
	return;
}

/////////////////////////////////////////////////////////////////////////////
// CAtari800WinView diagnostics

#ifdef _DEBUG
void CAtari800WinView::AssertValid() const
{
	CView::AssertValid();
}

void CAtari800WinView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CAtari800WinDoc* CAtari800WinView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CAtari800WinDoc)));
	return (CAtari800WinDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CAtari800WinView message handlers

BOOL CAtari800WinView::OnEraseBkgnd(CDC* pDC) 
{
	//Don't need to erase...I always have something that covers the whole window
	return TRUE;
}

void CAtari800WinView::OnDestroy() 
{
	ulAtariState = ATARI_UNINITIALIZED | ATARI_PAUSED | ATARI_CLOSING;
#ifdef ATARI_UI
	TerminateUIThread();
#endif
	//Clear last boot failed message so on reboot we don't think we crashed
	ulMiscStates &= ~ATARI_LAST_BOOT_FAILED;
	WriteRegDWORD( NULL, REG_MISC_STATES, ulMiscStates );
	WriteRegDWORD( NULL, REG_ENABLE_SIO_PATCH, enable_sio_patch );

	Atari_Exit( 0 );

	if( h_screenDC && hWnd )
		::ReleaseDC( hWnd, h_screenDC );
	h_screenDC = NULL;
	hWnd = NULL;

	CView::OnDestroy();
}


void CAtari800WinView::OnPrint(CDC* pDC, CPrintInfo* pInfo) 
{
	int		xRes, nMult;
	int		nWidth, nHeight;

	nWidth = lpbmi->bmiHeader.biWidth;
	nHeight = lpbmi->bmiHeader.biHeight;
	lpbmi->bmiHeader.biWidth = ATARI_WIDTH;
	lpbmi->bmiHeader.biHeight = -ATARI_HEIGHT;

	//Find the largest integral multiple of width on the page
	xRes = pDC->GetDeviceCaps( HORZRES );
	nMult = xRes / ATARI_WIDTH;
	//And stretch the original DIB to that size
	StretchDIBits( pDC->m_hDC, 0, 0, ATARI_WIDTH * nMult, ATARI_HEIGHT * nMult,
					0, 0, ATARI_WIDTH, ATARI_HEIGHT,
					atari_screen, lpbmi, DIB_RGB_COLORS, SRCCOPY );

	lpbmi->bmiHeader.biWidth = nWidth;
	lpbmi->bmiHeader.biHeight = nHeight;
//	CView::OnPrint(pDC, pInfo);
}

void CAtari800WinView::OnKillFocus(CWnd* pNewWnd) 
{
	if( ulAtariState & ATARI_RUNNING )
	{
		unlosttime = timeGetTime();
		Clear_Sound( FALSE );
		ulAtariState |= ATARI_NOFOCUS;
		DescribeAtariSystem();
		Reset_Keys();
	}

	CView::OnKillFocus(pNewWnd);
}

void CAtari800WinView::OnSetFocus(CWnd* pOldWnd) 
{
#ifdef USE_PERF_COUNTER
	LARGE_INTEGER	lnTime;
	ULONG	ulTimerLastVal = ulAtari_HW_Nexttime;
#endif

	if( ulAtariState & ATARI_LOAD_FAILED )
		return;

	if( ulAtariState & ATARI_CRASHED )
	{
		DescribeAtariSystem();
		return;
	}
	Restart_Sound( );

#ifdef USE_PERF_COUNTER
	QueryPerformanceCounter( &lnTime );
	ulAtari_HW_Nexttime = lnTime.LowPart + ulDeltaT;
	if( ulTimerLastVal > ulAtari_HW_Nexttime )
		bTimerRollover = TRUE;
	else
		bTimerRollover = FALSE;
#else
	ulAtari_HW_Nexttime = timeGetTime() + ulDeltaT;
#endif

	ulAtariState &= ~ATARI_NOFOCUS;
	DescribeAtariSystem();

	CView::OnSetFocus(pOldWnd);
}

BOOL CAtari800WinView::OnPreparePrinting(CPrintInfo* pInfo) 
{
	return DoPreparePrinting(pInfo);
}

void CAtari800WinView::OnInitialUpdate() 
{
	CView::OnInitialUpdate();
	
	AfxGetMainWnd()->SetWindowPos( &wndNoTopMost, 0, 0, 
		ATARI_VIS_WIDTH + GetSystemMetrics( SM_CXDLGFRAME )*2 + GetSystemMetrics( SM_CXEDGE )*2,
		ATARI_HEIGHT + GetSystemMetrics( SM_CYMENU) + nStatusSize + GetSystemMetrics( SM_CYDLGFRAME )*2 +		
		GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYEDGE ) * 2, SWP_NOMOVE | SWP_SHOWWINDOW );
}
