// ThView.cpp : implementation of the CThView class
//

#include "stdafx.h"
#include "Th.h"

#include "ThDoc.h"
#include "ThView.h"

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

/////////////////////////////////////////////////////////////////////////////
// CThView

IMPLEMENT_DYNCREATE(CThView, CFormView)

BEGIN_MESSAGE_MAP(CThView, CFormView)
	//{{AFX_MSG_MAP(CThView)
	ON_BN_CLICKED(IDC_ADDBAR, OnAddbar)
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_REMOVE, OnRemove)
	//}}AFX_MSG_MAP
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CThView construction/destruction

CThView::CThView()
	: CFormView(CThView::IDD)
{
	//{{AFX_DATA_INIT(CThView)
	//}}AFX_DATA_INIT
	// TODO: add construction code here

}

CThView::~CThView()
{
	int i;
	Bar* pBar;

	// Terminate child threads.  This method is OK for this type
	// of thread.
	for( i=0; i < Bars.GetSize(); i++ )
	{
		pBar = Bars.GetAt(i);
		pBar->Freeze();
		while( pBar->GetThreading() )
			;
		delete Bars.GetAt(i);
	}

	Bars.RemoveAll();
}

void CThView::DoDataExchange(CDataExchange* pDX)
{
	CFormView::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CThView)
	DDX_Control(pDX, IDC_AREABM, m_Area);
	//}}AFX_DATA_MAP
}

BOOL CThView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CFormView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CThView printing

BOOL CThView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CThView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CThView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}

void CThView::OnPrint(CDC* pDC, CPrintInfo*)
{
	// TODO: add code to print the controls
}

/////////////////////////////////////////////////////////////////////////////
// CThView diagnostics

#ifdef _DEBUG
void CThView::AssertValid() const
{
	CFormView::AssertValid();
}

void CThView::Dump(CDumpContext& dc) const
{
	CFormView::Dump(dc);
}

CThDoc* CThView::GetDocument() // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CThDoc)));
	return (CThDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CThView message handlers

// The code that will be threaded.
// Note how this code communicates with other code (the main thread) via 
// SetThreading() and CanMove()
UINT BarThreadProc( LPVOID pParam )
{
    Bar* pBar = (Bar*)pParam;

    if (pBar == NULL )
        return( 1 );

	pBar->SetThreading( 1 ); // Tell the Bar it is being handled by the thread.
	
	int i=0;
	while( pBar->CanMove() )  // While the Bar can move,
	{
		pBar->Move();  // Move it
		Sleep(1); //Introduce a delay, otherwise things look like crap
	}

	pBar->SetThreading( 0 );  // We're about to leave the thread, tell the object, it's no longer in use by the thread

    return 0;
}

// Response to user hitting the Add button
void CThView::OnAddbar() 
{
	CWinThread * pThread;
	Bar * pBar;
	pBar = new Bar;		// Create a new bar

	if( !pBar )
	{
		MessageBox( "Unable to create new bar");
		return;
	}

	Bars.Add( pBar );	// Add it to the list
	
	pThread = AfxBeginThread( BarThreadProc, pBar ); // Start the thread

	// pThread isn't really needed in this demo, but we could do things like change
	// the thread's proiority with it, if we want to.
	
	// Give the thread a tenth of a second to start, and then check to make sure it's running,
	// by checking the Threading status of the Bar
	Sleep(1);
	if( !pBar->GetThreading() )
		MessageBox( "Error starting thread!");

}

// Removes a Bar, and it's thread.
void CThView::OnRemove() 
{
	int i;
	Bar * pBar;
	i = Bars.GetSize();
	if( i )
	{
		pBar = Bars.GetAt(i-1);
		
		// Note how we control the thread, via the Bar object
		pBar->Freeze(); // Tell the thread to stop
		while( pBar->GetThreading() ) // Wait for thread to shutdown
			;
		Bars.RemoveAt( i-1 );// Get the bar out of the list,
		delete pBar; // And finally remove the bar from memory.
	}
}


// OnDraw - Prepares the memory DC, and BitBlt's it to the screen
// This OnDraw simply copies the memory DC over the current DC, using BitBlt
void CThView::OnDraw(CDC* pDC) 
{
	CRect Rect;
	
	m_Area.GetWindowRect( &Rect );
	ScreenToClient( &Rect );
	pDC->BitBlt( Rect.left, Rect.top, Rect.right-Rect.left, Rect.bottom-Rect.top, &memDC, 0, 0, SRCCOPY );
}

// OnInitialUpdate - Prepares our memory DC, pens, and Timer
void CThView::OnInitialUpdate() 
{
	CFormView::OnInitialUpdate();
	
	Area.LoadBitmap( IDB_AREA );

	memDC.CreateCompatibleDC( NULL );
	memDC.SelectObject( &Area );
	WhiteBrush.CreateSolidBrush( RGB(255, 255,255 ) );
	WhitePen.CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
	RedPen.CreatePen( PS_SOLID, 1, RGB(255, 0, 0 ) );
	RedBrush.CreateSolidBrush( RGB(255, 0, 0 ) );
	OldPen = memDC.SelectObject( &WhitePen );
	OldBrush = memDC.SelectObject( &RedBrush );
	memDC.SelectObject( &RedPen );
	
	SetTimer( 123, 100, NULL );

}

// Cleanup, on destroy
BOOL CThView::DestroyWindow() 
{
	while( Bars.GetSize() ) // Not really needed, but neat.
		OnRemove();

	KillTimer( 1000 ); // Kill the update timer

	return CFormView::DestroyWindow();
}

// The timer event, simply updates the screen.  It doesn't change the Bars, the threads
// do that.
void CThView::OnTimer(UINT nIDEvent) 
{
	CRect Rect;
	int i, Height, Width;
	
	m_Area.GetWindowRect( &Rect );
	ScreenToClient( &Rect );
	memDC.SelectObject( &WhitePen );
	memDC.SelectObject( &WhiteBrush );
	memDC.Rectangle( &Rect ); 
	if( Bars.GetSize() )
	{
		Width = (Rect.right-Rect.left)/Bars.GetSize();
		memDC.SelectObject( &RedPen );
		memDC.SelectObject( &RedBrush );
		for( i=0; i < Bars.GetSize(); i++ )
		{
			Height = Bars.GetAt(i)->GetHeight( Rect.bottom - Rect.top );
			memDC.Rectangle( Rect.left+i*Width, Rect.bottom-Height, Rect.left+i*Width+(Width/2), Rect.bottom  );
		}
	}	
	
	InvalidateRect( &Rect, FALSE );
	CFormView::OnTimer(nIDEvent);
}

