/////////////////////////////////////////////////////////////////////////////
// Name:        antiflickpl.cpp
// Purpose:     Double-buffering plugin class for reducing flickering.
// Author:      Aleksandras Gluchovas (@Lithuania)
// Modified by:
// Created:     23/10/98
// RCS-ID:      $Id: antiflickpl.cpp,v 1.2 2002/01/21 22:34:41 JS Exp $
// Copyright:   (c) Aleksandras Gluchovas
// Licence:   	wxWindows licence
/////////////////////////////////////////////////////////////////////////////
 
#ifdef __GNUG__
    #pragma implementation "antiflickpl.h"
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/fl/antiflickpl.h"

/***** Implementation for class cbAntiflickerPlugin *****/

IMPLEMENT_DYNAMIC_CLASS( cbAntiflickerPlugin, cbPluginBase )

BEGIN_EVENT_TABLE( cbAntiflickerPlugin, cbPluginBase )

	EVT_PL_START_DRAW_IN_AREA  ( cbAntiflickerPlugin::OnStartDrawInArea  )
	EVT_PL_FINISH_DRAW_IN_AREA ( cbAntiflickerPlugin::OnFinishDrawInArea )

END_EVENT_TABLE()

// initialization of static members

int cbAntiflickerPlugin::mRefCount    = 0;

wxBitmap*   cbAntiflickerPlugin::mpVertBuf    = 0;
wxBitmap*   cbAntiflickerPlugin::mpHorizBuf   = 0;
wxMemoryDC* cbAntiflickerPlugin::mpVertBufDc  = 0;
wxMemoryDC* cbAntiflickerPlugin::mpHorizBufDc = 0;

// constructors

cbAntiflickerPlugin::cbAntiflickerPlugin(void)
	: mpLRUBufDc  ( NULL ),
	  mLRUArea    ( -1,-1, -1,-1 )
{
	++mRefCount;
}

cbAntiflickerPlugin::cbAntiflickerPlugin( wxFrameLayout* pPanel, int paneMask )

	: cbPluginBase( pPanel, paneMask ),
	  mpLRUBufDc  ( NULL ),
	  mLRUArea    ( -1,-1, -1,-1 )
{
	++mRefCount;
}

cbAntiflickerPlugin::~cbAntiflickerPlugin()
{
	if ( --mRefCount == 0 )
	{
		if ( mpHorizBuf )
		{
			mpHorizBufDc->SelectObject( wxNullBitmap );
			delete mpHorizBuf;
			delete mpHorizBufDc;
			mpHorizBuf   = 0;
			mpHorizBufDc = 0;
		}

		if ( mpVertBuf ) 
		{
			mpVertBufDc->SelectObject( wxNullBitmap );
			delete mpVertBuf; 
			delete mpVertBufDc;
			mpVertBuf   = 0;
			mpVertBufDc = 0;
		}
	}
}

wxDC* cbAntiflickerPlugin::FindSuitableBuffer( const wxRect& forArea )
{
	if ( mpVertBuf )
	{
		if ( mpVertBuf->GetHeight() >= forArea.height &&
			 mpVertBuf->GetWidth()  >= forArea.width )

			 return mpVertBufDc;
	}
	else
	if ( mpHorizBuf )
	{
		if ( mpHorizBuf->GetHeight() >= forArea.height &&
			 mpHorizBuf->GetWidth()  >= forArea.width )

			 return mpHorizBufDc;
	}

	return 0;
}

wxDC* cbAntiflickerPlugin::AllocNewBuffer( const wxRect& forArea )
{
	// TBD:: preallocate bit larger bitmap at once, to avoid
	//       excessive realocations later

	// check whether the given area is oriented horizontally
	// or vertically and choose corresponding bitmap to create or
	// recreate

	if ( forArea.height > forArea.width )
	{
		wxSize prevDim( 0,0 );

		if ( mpVertBuf ) 
		{
			prevDim.x = mpVertBuf->GetWidth();
			prevDim.y = mpVertBuf->GetHeight();

			mpVertBufDc->SelectObject( wxNullBitmap );
			delete mpVertBuf;
		}
		else
			mpVertBufDc = new wxMemoryDC();
		
		mpVertBuf = new wxBitmap( int( wxMax(forArea.width,  prevDim.x ) ), 
								  int( wxMax(forArea.height, prevDim.y ) )
								);

		mpVertBufDc->SelectObject( *mpVertBuf );

		return mpVertBufDc;
	}
	else
	{
		wxSize prevDim( 0,0 );

		if ( mpHorizBuf ) 
		{
			prevDim.x = mpHorizBuf->GetWidth();
			prevDim.y = mpHorizBuf->GetHeight();

			mpHorizBufDc->SelectObject( wxNullBitmap );
			delete mpHorizBuf;
		}
		else
			mpHorizBufDc = new wxMemoryDC();
		
		mpHorizBuf = new wxBitmap( int( wxMax(forArea.width,  prevDim.x ) ), 
								   int( wxMax(forArea.height, prevDim.y ) )
								 );

		mpHorizBufDc->SelectObject( *mpHorizBuf );

		return mpHorizBufDc;
	}
}

void cbAntiflickerPlugin::OnStartDrawInArea( cbStartDrawInAreaEvent& event )
{
	wxASSERT( mpLRUBufDc == NULL ); // DBG:: see comments in OnFinishDrawInArea(..) method

	// short-cut
	wxRect& area = event.mArea;

	if ( event.mArea.width  < 0 || 
		 event.mArea.height < 0 ) return;

	// memorize given area
	mLRUArea.x      = area.x;
	mLRUArea.y      = area.y;
	mLRUArea.width  = area.width;
	mLRUArea.height = area.height;

	wxDC* pBufDc = FindSuitableBuffer( area );

	if ( !pBufDc )
	
		pBufDc = AllocNewBuffer( area );

	pBufDc->SetDeviceOrigin( -area.x, -area.y );

	pBufDc->SetClippingRegion( area.x,     area.y,
							   area.width, area.height );

	wxClientDC clntDc( &mpLayout->GetParentFrame() );

	(*event.mppDc) = pBufDc;

	mpLRUBufDc = pBufDc; // memorize buffer, which will be flushed to screen
	                     // upon "commiting" the drawing

	/*
	// OLD STUFF::
	mpLRUBufDc->Blit( pos.x, pos.y, size.x, size.y,
				      &clntDc, pos.x, pos.y, wxCOPY );
					*/
}

void cbAntiflickerPlugin::OnFinishDrawInArea( cbFinishDrawInAreaEvent& event )
{
	wxRect& area = event.mArea;

	if ( event.mArea.width  < 0 || 
		 event.mArea.height < 0 ) return;

	wxASSERT( mpLRUBufDc ); // DBG:: OnStartDrawInArea should be called first

	// FOR NOW:: OnStartDrawInArea(..) should be immediately followed
	//           by OnFinishDrawInArea(..) for the same area

	wxASSERT( mLRUArea.x      == area.x      );
	wxASSERT( mLRUArea.y      == area.y      );
	wxASSERT( mLRUArea.width  == area.width  );
	wxASSERT( mLRUArea.height == area.height );

	wxClientDC clntDc( &mpLayout->GetParentFrame() );

	// "commit" drawings in one-shot
	clntDc.Blit( area.x, area.y, area.width, area.height,
				 mpLRUBufDc, area.x, area.y, wxCOPY );

	mpLRUBufDc->DestroyClippingRegion();
	mpLRUBufDc = 0;
}

