#include <apps/Common/wxAbstraction.h>
#include <wx/clipbrd.h>
#include "AnalysisDataGridTable.h"
#include "DrawingCanvas.h"
#include "ImageCanvas.h"

using namespace mvIMPACT::acquire;

//=============================================================================
//================= Implementation DrawingCanvas ==============================
//=============================================================================
IMPLEMENT_DYNAMIC_CLASS(DrawingCanvas, wxScrolledWindow)

BEGIN_EVENT_TABLE(DrawingCanvas, wxScrolledWindow)
  EVT_SIZE(DrawingCanvas::OnSize)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
DrawingCanvas::DrawingCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "DrawingCanvas" */, bool boActive /* = false */ )
  : wxScrolledWindow( parent, id, pos, size, style, name ), m_boActive(boActive)
//-----------------------------------------------------------------------------
{

}

//-----------------------------------------------------------------------------
void DrawingCanvas::HandleSizeEvent( wxSizeEvent& e )
//-----------------------------------------------------------------------------
{
	Refresh( true );
	e.Skip();
}

//-----------------------------------------------------------------------------
void DrawingCanvas::OnSize( wxSizeEvent& e )
//-----------------------------------------------------------------------------
{
	HandleSizeEvent( e );
}

//-----------------------------------------------------------------------------
void DrawingCanvas::SetActive( bool boActive )
//-----------------------------------------------------------------------------
{
	m_boActive = boActive;
	Refresh( !boActive );
}

//=============================================================================
//================= Implementation PlotCanvas =================================
//=============================================================================
//-----------------------------------------------------------------------------
PlotCanvas::PlotCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */, const wxSize& size /* = wxDefaultSize */,
	long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "DrawingCanvas" */, bool boActive /* = false */ )
  : DrawingCanvas( parent, id, pos, size, style, name, boActive ), m_BorderWidth(25), m_ImageCount(-1), m_UpdateFrequency(3)
//-----------------------------------------------------------------------------
{

}

//-----------------------------------------------------------------------------
void PlotCanvas::SetBorderWidth( int borderWidth )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	m_BorderWidth = borderWidth;
}

//-----------------------------------------------------------------------------
bool PlotCanvas::SetUpdateFrequency( int frequency )
//-----------------------------------------------------------------------------
{
	if( ( frequency >= ulMin ) && ( frequency <= ulMax ) )
	{
		m_UpdateFrequency = frequency;
		return true;
	}
	return false;
}

//=============================================================================
//================= Implementation GridWithClipboardFeature ===================
//=============================================================================
IMPLEMENT_DYNAMIC_CLASS(GridWithClipboardFeature, wxGrid)

BEGIN_EVENT_TABLE(GridWithClipboardFeature, wxGrid)
  EVT_GRID_CELL_RIGHT_CLICK(GridWithClipboardFeature::OnCellRightClick)
  EVT_MENU(GRID_TO_CLIPBOARD, GridWithClipboardFeature::OnGridToClipboard)
  EVT_MENU(GRID_SELECTION_TO_CLIPBOARD, GridWithClipboardFeature::OnGridSelectionToClipboard)
  EVT_MENU(SET_GRID_VALUE_FORMAT_STRING, GridWithClipboardFeature::OnSetGridValueFormatString)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
GridWithClipboardFeature::GridWithClipboardFeature( wxWindow* parent, wxWindowID id, const wxPoint& pos /* = wxDefaultPosition*/,
												   const wxSize& size /* = wxDefaultSize */, long style /* = wxWANTS_CHARS */,
												   const wxString& name /* = wxPanelNameStr */ ) : wxGrid(parent, id, pos, size, style, name), pDataProvider_(0)

//-----------------------------------------------------------------------------
{

}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::OnCellRightClick( wxGridEvent& e )
//-----------------------------------------------------------------------------
{
	wxMenu menu(wxT(""));
	menu.Append( GRID_TO_CLIPBOARD, wxT("&Copy grid to clipboard") );
	//menu.Append( GRID_SELECTION_TO_CLIPBOARD, wxT("Copy grid &selection to clipboard") );
	menu.Append( SET_GRID_VALUE_FORMAT_STRING, wxT("&Set grid value format string") );
	PopupMenu( &menu, e.GetPosition() );
}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::OnGridToClipboard( wxCommandEvent& )
//-----------------------------------------------------------------------------
{
	SetClipboardData( 0, GetNumberRows(), 0, GetNumberCols() );
}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::OnGridSelectionToClipboard( wxCommandEvent& )
//-----------------------------------------------------------------------------
{

}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::OnSetGridValueFormatString( wxCommandEvent& )
//-----------------------------------------------------------------------------
{
	wxString newFormatString = ::wxGetTextFromUser( wxT("Enter the new format string for this grid(printf style. For the sake of flexibility everything\nis allowed here, but weird/invalid(e.g. %.2y) inputs might cause crashes)."),
			wxT("New format string"),
			pDataProvider_->GetGridValueFormatString(),
			this );
	pDataProvider_->SetGridValueFormatString( newFormatString );
}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::SetClipboardData( int startRow, int rows, int startCol, int cols )
//-----------------------------------------------------------------------------
{
	if( ( rows <= 0 ) || ( cols <= 0 ) )
	{
		return;
	}

	wxString data;
	for( int row=startRow; row<rows; row++ )
	{
		for( int col=startCol; col<cols; col++ )
		{
			data.Append( pDataProvider_->GetGridValue( row, col ) );
			if( col < cols-1 )
			{
				data.Append( wxT(", ") );
			}
		}
		if( row < rows-1 )
		{
			data.Append( wxT("\n") );
		}
	}

	if( wxTheClipboard->Open() )
	{
		wxTheClipboard->SetData( new wxTextDataObject(data) );
		/* bool boResult = */ wxTheClipboard->Flush();
		wxTheClipboard->Close();
	}
}

//-----------------------------------------------------------------------------
void GridWithClipboardFeature::SetDataProvider( ImageAnalysisPlotCanvas* p )
//-----------------------------------------------------------------------------
{
	pDataProvider_ = p;
}

//=============================================================================
//================= Implementation ImageAnalysisPlotCanvas ====================
//=============================================================================
const wxColour ImageAnalysisPlotCanvas::m_ADarkerKindOfGreen = wxColour(0, 150, 0);
const wxColour ImageAnalysisPlotCanvas::m_CyanLight = wxColour(0, 200, 200);
const wxColour ImageAnalysisPlotCanvas::m_Magenta = wxColour(255, 0, 255);
const wxColour ImageAnalysisPlotCanvas::m_MagentaLight = wxColour(200, 0, 200);

//-----------------------------------------------------------------------------
ImageAnalysisPlotCanvas::ImageAnalysisPlotCanvas( wxWindow* parent, const wxString& configName, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */, const wxSize& size /* = wxDefaultSize */,
	long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "DrawingCanvas" */, bool boActive /* = false */, int channelCount /* = 0 */ ) :
	PlotCanvas(parent, id, pos, size, style, name, boActive), m_AOIColour(*wxBLUE),
	m_pImageCanvas(0), m_ConfigName(configName), m_boAOIFullMode(false), m_boProcessBayerParity(false), m_DisplayMethod(dmGraphical),
	m_valCount(VAL_COUNT_8_BIT), m_AOIx(0), m_AOIy(0), m_AOIw(640), m_AOIh(480), m_pAOI(0), m_HistoryDepth(20), m_GridStepsX(1), m_GridStepsY(1),
	m_ChannelCount(channelCount), m_pixelFormat(ibpfRaw), m_bayerParity(bmpUndefined), m_boUnsupportedPixelFormat(false), m_DrawStart_percent(0), m_DrawWindow_percent(100),
	m_pNumericalDisplay(0)
//-----------------------------------------------------------------------------
{
	SetDefaultPens( ibpfMono8 );
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::CreateGrid( wxWindow* pParent, wxWindowID gridID )
//-----------------------------------------------------------------------------
{
	m_pNumericalDisplay = new GridWithClipboardFeature(pParent, gridID);
	m_pNumericalDisplay->SetDataProvider( this );
	CreateGridCustom();
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::CreateGridCustom( void )
//-----------------------------------------------------------------------------
{
	m_pNumericalDisplay->SetTable( new AnalysisDataGridTable(this, GetRowCountNeededForNumericalDisplay(), m_ChannelCount), true );
}

//-----------------------------------------------------------------------------
int ImageAnalysisPlotCanvas::GetRowCountNeededForNumericalDisplay( void ) const
//-----------------------------------------------------------------------------
{
	return m_valCount + 1;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetActive( bool boActive )
//-----------------------------------------------------------------------------
{
	if( boActive == IsActive() )
	{
		return;
	}

	m_boActive = boActive;
	if( m_pImageCanvas )
	{
		if( boActive )
		{
			m_pAOI = m_pImageCanvas->RegisterAOI( wxRect(m_AOIx, m_AOIy, m_AOIw, m_AOIh), m_AOIColour, this );
		}
		else
		{
			m_pImageCanvas->RemoveAOI( this );
			m_pAOI = 0;
		}
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetDefaultPens( mvIMPACT::acquire::TImageBufferPixelFormat pixelFormat )
//-----------------------------------------------------------------------------
{
	switch( pixelFormat )
	{
	case ibpfYUV422_UYVYPacked:
	case ibpfYUV422_UYVY_10Packed:
	case ibpfYUV444_UYVPacked:
	case ibpfYUV444_UYV_10Packed:
		m_Pens[pChannel0Hor].pColour_ = wxCYAN;
		m_Pens[pChannel0Ver].pColour_ = wxBLACK;
		m_Pens[pChannel1Hor].pColour_ = &m_Magenta;
		m_Pens[pChannel1Ver].pColour_ = &m_CyanLight;
		m_Pens[pChannel2Hor].pColour_ = wxLIGHT_GREY;
		m_Pens[pChannel2Ver].pColour_ = &m_MagentaLight;
		m_Pens[pChannel3Hor].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pChannel3Ver].pColour_ = wxGREEN;
		break;
	case ibpfYUV422Packed:
	case ibpfYUV444Planar:
	case ibpfYUV422Planar:
	case ibpfYUV422_10Packed:
	case ibpfYUV444Packed:
	case ibpfYUV444_10Packed:
		m_Pens[pChannel0Hor].pColour_ = wxBLACK;
		m_Pens[pChannel0Ver].pColour_ = wxCYAN;
		m_Pens[pChannel1Hor].pColour_ = &m_Magenta;
		m_Pens[pChannel1Ver].pColour_ = wxLIGHT_GREY;
		m_Pens[pChannel2Hor].pColour_ = &m_CyanLight;
		m_Pens[pChannel2Ver].pColour_ = &m_MagentaLight;
		m_Pens[pChannel3Hor].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pChannel3Ver].pColour_ = wxGREEN;
		break;
	case ibpfMono8:
	case ibpfMono16:
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono12Packed_V2:
	case ibpfMono14:
	case ibpfMono32:
	case ibpfRaw:
		m_Pens[pChannel0Hor].pColour_ = wxBLACK;
		m_Pens[pChannel0Ver].pColour_ = wxGREEN;
		m_Pens[pChannel1Hor].pColour_ = wxBLUE;
		m_Pens[pChannel1Ver].pColour_ = wxLIGHT_GREY;
		m_Pens[pChannel2Hor].pColour_ = wxCYAN;
		m_Pens[pChannel2Ver].pColour_ = wxRED;
		m_Pens[pChannel3Hor].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pChannel3Ver].pColour_ = &m_Magenta;
		break;
	case ibpfBGR888Packed:
	case ibpfBGR101010Packed_V2:
	case ibpfRGBx888Packed:
	case ibpfRGBx888Planar:
	case ibpfRGB888Packed:
	case ibpfRGB101010Packed:
	case ibpfRGB121212Packed:
	case ibpfRGB141414Packed:
	case ibpfRGB161616Packed:
	default:
		m_Pens[pChannel0Hor].pColour_ = wxRED;
		m_Pens[pChannel0Ver].pColour_ = wxGREEN;
		m_Pens[pChannel1Hor].pColour_ = wxBLUE;
		m_Pens[pChannel1Ver].pColour_ = wxLIGHT_GREY;
		m_Pens[pChannel2Hor].pColour_ = wxCYAN;
		m_Pens[pChannel2Ver].pColour_ = wxBLACK;
		m_Pens[pChannel3Hor].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pChannel3Ver].pColour_ = &m_Magenta;
		break;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetBayerDescriptions( mvIMPACT::acquire::TBayerMosaicParity bayerParity )
//-----------------------------------------------------------------------------
{
	switch( bayerParity )
	{
	case bmpBG:
		m_Pens[pEvenEven].description_ = wxString(wxT("B"));
		m_Pens[pEvenOdd].description_ = wxString(wxT("G(blue)"));
		m_Pens[pOddEven].description_ = wxString(wxT("G(red)"));
		m_Pens[pOddOdd].description_ = wxString(wxT("R"));
		break;
	case bmpGB:
		m_Pens[pEvenEven].description_ = wxString(wxT("G(blue)"));
		m_Pens[pEvenOdd].description_ = wxString(wxT("B"));
		m_Pens[pOddEven].description_ = wxString(wxT("R"));
		m_Pens[pOddOdd].description_ = wxString(wxT("G(red)"));
		break;
	case bmpGR:
		m_Pens[pEvenEven].description_ = wxString(wxT("G(red)"));
		m_Pens[pEvenOdd].description_ = wxString(wxT("R"));
		m_Pens[pOddEven].description_ = wxString(wxT("B"));
		m_Pens[pOddOdd].description_ = wxString(wxT("G(blue)"));
		break;
	case bmpRG:
		m_Pens[pEvenEven].description_ = wxString(wxT("R"));
		m_Pens[pEvenOdd].description_ = wxString(wxT("G(red)"));
		m_Pens[pOddEven].description_ = wxString(wxT("G(blue)"));
		m_Pens[pOddOdd].description_ = wxString(wxT("B"));
		break;
	default:
		break;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetBayerPens( mvIMPACT::acquire::TImageBufferPixelFormat pixelFormat, mvIMPACT::acquire::TBayerMosaicParity bayerParity )
//-----------------------------------------------------------------------------
{
	switch( bayerParity )
	{
	case bmpBG:
		m_Pens[pEvenEven].pColour_ = wxBLUE;
		m_Pens[pEvenOdd].pColour_ = wxGREEN;
		m_Pens[pOddEven].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pOddOdd].pColour_ = wxRED;
		break;
	case bmpGB:
		m_Pens[pEvenEven].pColour_ = wxGREEN;
		m_Pens[pEvenOdd].pColour_ = wxBLUE;
		m_Pens[pOddEven].pColour_ = wxRED;
		m_Pens[pOddOdd].pColour_ = &m_ADarkerKindOfGreen;
		break;
	case bmpGR:
		m_Pens[pEvenEven].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pEvenOdd].pColour_ = wxRED;
		m_Pens[pOddEven].pColour_ = wxBLUE;
		m_Pens[pOddOdd].pColour_ = wxGREEN;
		break;
	case bmpRG:
		m_Pens[pEvenEven].pColour_ = wxRED;
		m_Pens[pEvenOdd].pColour_ = &m_ADarkerKindOfGreen;
		m_Pens[pOddEven].pColour_ = wxGREEN;
		m_Pens[pOddOdd].pColour_ = wxBLUE;
		break;
	default:
		SetDefaultPens( pixelFormat );
		break;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetupGDIData( const mvIMPACT::acquire::ImageBuffer* pIB, int channelCount, mvIMPACT::acquire::TBayerMosaicParity bayerParity )
//-----------------------------------------------------------------------------
{
	if( bayerParity == bmpUndefined )
	{
		for( int channel=0; channel<channelCount; channel++ )
		{
			if( channel < PLANE_CNT )
			{
				switch( pIB->pixelFormat )
				{
				case ibpfBGR888Packed:
				case ibpfBGR101010Packed_V2:
					m_Pens[channel].description_ = ConvertedString(pIB->pChannels[pIB->iChannelCount - 1 - channel].szChannelDesc);
					break;
				default:
					m_Pens[channel].description_ = ConvertedString(pIB->pChannels[channel].szChannelDesc);
					break;
				}
			}
		}
		SetDefaultPens( pIB->pixelFormat );
	}
	else
	{
		SetBayerDescriptions( bayerParity );
		SetBayerPens( pIB->pixelFormat, bayerParity );
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetDisplayMethod( TDisplayMethod displayMethod )
//-----------------------------------------------------------------------------
{
	if( displayMethod == m_DisplayMethod )
	{
		return;
	}

	m_DisplayMethod = displayMethod;
	UpdateAnalysisOutput( true );
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetAOIFullMode( bool boActive )
//-----------------------------------------------------------------------------
{
	m_boAOIFullMode = boActive;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetHistoryDepth( unsigned int value )
//-----------------------------------------------------------------------------
{
	if( value < 1 )
	{
		return;
	}
	if( AssignDisplayParameter( m_HistoryDepth, value ) )
	{
		UpdateInternalData();
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetDrawStartPercent( int value_percent )
//-----------------------------------------------------------------------------
{
	if( ( value_percent < 0 ) || ( value_percent > 99 ) )
	{
		return;
	}
	AssignDisplayParameter( m_DrawStart_percent, value_percent );
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetDrawWindowWidthPercent( int value_percent )
//-----------------------------------------------------------------------------
{
	if( ( value_percent < 1 ) || ( value_percent > 100 ) )
	{
		return;
	}
	AssignDisplayParameter( m_DrawWindow_percent, value_percent );
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::GetDrawRange( unsigned int* pFrom, unsigned int* pTo ) const
//-----------------------------------------------------------------------------
{
	unsigned int from = static_cast<unsigned int>( static_cast<double>(m_valCount) * (static_cast<double>(m_DrawStart_percent) / 100.));
	unsigned int to = from + static_cast<unsigned int>( static_cast<double>(m_valCount-1) * (static_cast<double>(GetPercentageToDraw()) / 100.));
	if( pFrom )
	{
		*pFrom = from;
	}
	if( pTo )
	{
		*pTo = to;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetProcessBayerParity( bool boActive )
//-----------------------------------------------------------------------------
{
	m_boProcessBayerParity = boActive;
}

//-----------------------------------------------------------------------------
wxGrid* ImageAnalysisPlotCanvas::GetGrid( void ) const
//-----------------------------------------------------------------------------
{
	return m_pNumericalDisplay;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::HandleSizeEvent( wxSizeEvent& )
//-----------------------------------------------------------------------------
{
	if( GetDisplayMethod() == ImageAnalysisPlotCanvas::dmGraphical )
	{
		// otherwise not every window message will correctly repaint the window
		UpdateAnalysisOutput( true );
	}
}

//-----------------------------------------------------------------------------
bool ImageAnalysisPlotCanvas::MustUpdate( const ImageBuffer* pIB, int plotChannelCount, int x, int y, int w, int h, bool boForceRefresh, bool* pboAOIChanged /* = 0 */, bool* pboImageFormatChanged /* = 0 */ )
//-----------------------------------------------------------------------------
{
	if( !pIB || !pIB->vpData || !IsActive() || ( pIB->iWidth == 0 ) || ( pIB->iHeight == 0 ) )
	{
		return false;
	}

	bool boResult = false;

	if( ( m_ChannelCount != plotChannelCount ) || ( m_pixelFormat != pIB->pixelFormat ) )
	{
		boResult = true;
		if( pboImageFormatChanged )
		{
			*pboImageFormatChanged = true;
		}
		m_pixelFormat = pIB->pixelFormat;
		m_boUnsupportedPixelFormat = false;
	}

	if( SetAOI( pIB, x, y, w, h ) )
	{
		if( pboAOIChanged )
		{
			*pboAOIChanged = true;
		}
		boResult = true;
	}
	else if( pboAOIChanged )
	{
		*pboAOIChanged = false;
	}

	if( ( ( m_ImageCount++ % m_UpdateFrequency ) == 0 ) || boForceRefresh )
	{
		boResult = true;
	}

	return boResult;
}

//-----------------------------------------------------------------------------
bool ImageAnalysisPlotCanvas::SetAOI( const ImageBuffer* pIB, int x, int y, int w, int h )
//-----------------------------------------------------------------------------
{
	bool boChanged = false;

	if( m_boAOIFullMode )
	{
		x = 0;
		y = 0;
		w = pIB->iWidth;
		h = pIB->iHeight;
	}

	// validate values
	// x-offset
	if( ( x >= 0 ) && ( m_AOIx != x ) )
	{
		m_AOIx = ( x >= pIB->iWidth ) ? 0 : x;
		boChanged = true;
	}
	else if( m_AOIx >= pIB->iWidth )
	{
		m_AOIx = 0;
		boChanged = true;
	}

	// y-offset
	if( ( y >= 0 ) && ( m_AOIy != y ) )
	{
		m_AOIy = ( y >= pIB->iHeight ) ? 0 : y;
		boChanged = true;
	}
	else if( m_AOIy >= pIB->iHeight )
	{
		m_AOIy = 0;
		boChanged = true;
	}

	// aoi width
	if( ( w > 0 ) && ( m_AOIw != w ) )
	{
		int newValue = ( w + m_AOIx > pIB->iWidth ) ? pIB->iWidth - m_AOIx : w;
		// if the AOI width is larger then x-offset + image width, the AOI width must be clipped
		// this might have been the case in the previous image as well and thus doesn't
		// implies that this is definitely a change -> check twice
		if( newValue != m_AOIw )
		{
			m_AOIw = newValue;
			boChanged = true;
		}
	}
	else if( ( m_AOIw + m_AOIx ) > pIB->iWidth )
	{
		m_AOIw = pIB->iWidth - m_AOIx;
		boChanged = true;
	}

	// aoi height
	if( ( h > 0 ) && ( m_AOIh != h ) )
	{
		int newValue = ( h + m_AOIy > pIB->iHeight ) ? pIB->iHeight - m_AOIy : h;
		// if the AOI height is larger then y-offset + image height, the AOI height must be clipped
		// this might have been the case in the previous image as well and thus doesn't
		// implies that this is definitely a change -> check twice
		if( newValue != m_AOIh )
		{
			m_AOIh = newValue;
			boChanged = true;
		}
	}
	else if( ( m_AOIh + m_AOIy ) > pIB->iHeight )
	{
		m_AOIh = pIB->iHeight - m_AOIy;
		boChanged = true;
	}

	if( m_pImageCanvas && ( m_pAOI != 0 ) && boChanged )
	{
		m_pImageCanvas->SetAOI( this, m_AOIx, m_AOIy, m_AOIw, m_AOIh );
	}

	return boChanged;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetAOIColour( const wxColour& c )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	m_AOIColour = c;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetGridSteps( int XSteps, int YSteps )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	if( XSteps > 0 )
	{
		m_GridStepsX = XSteps;
	}
	if( YSteps > 0 )
	{
		m_GridStepsY = YSteps;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::RegisterImageCanvas( ImageCanvas* p )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	m_pImageCanvas = p;
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::SetGridValueFormatString( const wxString& gridValueFormatString )
//-----------------------------------------------------------------------------
{
	m_GridValueFormatString = gridValueFormatString;
	UpdateAnalysisOutput( true );
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::UpdateAnalysisOutput( bool boEraseBackground )
//-----------------------------------------------------------------------------
{
	switch( m_DisplayMethod )
	{
	case dmGraphical:
		Refresh( boEraseBackground );
		break;
	case dmNumerical:
		UpdateGrid();
		break;
	}
}

//-----------------------------------------------------------------------------
void ImageAnalysisPlotCanvas::UpdateGrid( void )
//-----------------------------------------------------------------------------
{
	if( m_pNumericalDisplay )
	{
		m_pNumericalDisplay->Refresh();
	}
}
