#include "DrawingScope.h"
#include "HistogramCanvas.h"
#include "PropViewFrame.h"
#include <wx/dcbuffer.h>
#include <wx/settings.h>

using namespace mvIMPACT::acquire;

IMPLEMENT_DYNAMIC_CLASS(HistogramCanvas, DrawingCanvas)

BEGIN_EVENT_TABLE(HistogramCanvas, DrawingCanvas)
  EVT_PAINT(HistogramCanvas::OnPaint)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
HistogramCanvas::HistogramCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "histogram" */, bool boActive /* = true */ )
	: PlotCanvas( parent, id, pos, size, style, name, boActive, 1 ), m_ppHistogramBuffer(0), m_CurrentMax(1), m_XMarkerStepWidth(50) {}
 //-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
HistogramCanvas::~HistogramCanvas()
//-----------------------------------------------------------------------------
{
	DeallocateHistogramBuffer();
}

//-----------------------------------------------------------------------------
void HistogramCanvas::DeallocateHistogramBuffer( void )
//-----------------------------------------------------------------------------
{
	if( m_ppHistogramBuffer )
	{
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			delete [] m_ppHistogramBuffer[channel];
		}
		delete [] m_ppHistogramBuffer;
		m_ppHistogramBuffer = 0;
	}
}

//-----------------------------------------------------------------------------
void HistogramCanvas::DrawHistogramLine( wxPaintDC& dc, int h, double scaleX, double scaleY, TPlane plane )
//-----------------------------------------------------------------------------
{
	if( !m_ppHistogramBuffer )
	{
		return;
	}

	int lowerStart = h - GetBorderWidth();
	dc.SetPen( *m_Pens[plane] );
	unsigned int stepWidth = m_valCount / 256;
	for( unsigned int i=0; i<m_valCount-1; i+=stepWidth )
	{
		dc.DrawLine( static_cast<int>( GetBorderWidth() + ( i*scaleX ) + 1 ),
					 static_cast<int>( lowerStart - ( m_ppHistogramBuffer[plane][i] * scaleY ) ),
					 static_cast<int>( GetBorderWidth() + ( ( i+1 ) * scaleX ) + 1 ),
					 static_cast<int>( lowerStart - ( m_ppHistogramBuffer[plane][i+1] * scaleY ) ) );
	}
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void HistogramCanvas::OnPaint( wxPaintEvent & )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker( m_critSect );
	wxPaintDC dc( this );

	if( !IsActive() )
	{
		return;
	}

	DrawingScope scope(dc);
	wxCoord w, h;
	dc.GetSize(&w, &h);
	int borderWidth = GetBorderWidth();
	double scaleY = static_cast<double>(h-2*borderWidth)/m_CurrentMax;
	double scaleX = static_cast<double>(w-2*borderWidth)/m_valCount;

	dc.SetPen( *wxBLACK );
	dc.SetBrush( *wxWHITE_BRUSH );
	dc.DrawRectangle( 0, 0, w, h );
	dc.SetBrush( wxNullBrush );
	// lower line
	dc.DrawLine( borderWidth, h-borderWidth, w-borderWidth, h-borderWidth );
	// line on left side
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth, borderWidth );

	// markers for X-axis
	int markerStartHeight = h - borderWidth - 3;
	int markerEndHeight = h - borderWidth + 4;
	for( unsigned int i=0; i<m_valCount; i += m_XMarkerStepWidth )
	{
		dc.DrawLine( static_cast<int>( borderWidth + ( i * scaleX )), markerStartHeight, static_cast<int>( borderWidth + ( i * scaleX )), markerEndHeight );
		dc.DrawText( wxString::Format( wxT("%d"), i ), static_cast<int>( borderWidth + ( i * scaleX )), h - borderWidth + 3 );
	}

	// markers for y-axis
	int markerStart = borderWidth - 3;
	int markerEnd = borderWidth + 4;
	dc.DrawLine( markerStart, borderWidth, markerEnd, borderWidth );
	dc.DrawText( wxString::Format( wxT("%d"), m_CurrentMax ), 1, 1 );

	for( int channel=0; channel<m_ChannelCount; channel++ )
	{
		DrawHistogramLine( dc, h, scaleX, scaleY, static_cast<TPlane>(channel) );
	}
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void HistogramCanvas::RefreshData( const ImageBuffer* pIB, int x /* = -1 */, int y /* = -1 */, int w /* = -1 */, int h /* = -1 */, bool boForceRefresh /* = false */ )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker( m_critSect );

	bool boImageFormatChanged = false;
	if( !MustUpdate( pIB, x, y, w, h, boForceRefresh, 0, &boImageFormatChanged ) )
	{
		return;
	}

	if( boImageFormatChanged || !m_ppHistogramBuffer )
	{
		DeallocateHistogramBuffer();
		m_ChannelCount = pIB->iChannelCount;
		m_ppHistogramBuffer = new int*[pIB->iChannelCount];
		for( int channel=0; channel<pIB->iChannelCount; channel++ )
		{ 
			switch( pIB->pixelFormat )
			{
			case ibpfMono10:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_10_BIT];
				m_valCount = VAL_COUNT_10_BIT;
				break;
			case ibpfMono12:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_12_BIT];
				m_valCount = VAL_COUNT_12_BIT;
				break;
			case ibpfMono14:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_14_BIT];
				m_valCount = VAL_COUNT_14_BIT;
				break;
			case ibpfMono16:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_16_BIT];
				m_valCount = VAL_COUNT_16_BIT;
				break;
			default:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_8_BIT];
				m_valCount = VAL_COUNT_8_BIT;
				break;
			}
		}
		m_XMarkerStepWidth = m_valCount / 6;
	}

	for( int channel=0; channel<pIB->iChannelCount; channel++ )
	{ 
		memset( m_ppHistogramBuffer[channel], 0, m_valCount*sizeof(int) );
	}
	
	switch( pIB->pixelFormat )
	{
	case ibpfMono8:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				++m_ppHistogramBuffer[pRed][*pData++];
			}
		}
		break;
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono14:
	case ibpfMono16:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				++m_ppHistogramBuffer[pRed][*pData++];
			}
		}
		break;	
	case ibpfRGBx888Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				++m_ppHistogramBuffer[pBlue][*pData++];
				++m_ppHistogramBuffer[pGreen][*pData++];
				++m_ppHistogramBuffer[pRed][*pData++];
				++pData;
			}
		}
		break;
	case ibpfRGB888Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				++m_ppHistogramBuffer[pBlue][*pData++];
				++m_ppHistogramBuffer[pGreen][*pData++];
				++m_ppHistogramBuffer[pRed][*pData++];
			}
		}
		break;
	case ibpfRGBx888Planar:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pr = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pRed].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + m_AOIx;
			unsigned char* pg = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pGreen].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pGreen].iLinePitch ) + m_AOIx;
			unsigned char* pb = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pBlue].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pBlue].iLinePitch ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				++m_ppHistogramBuffer[pRed][*pr++];
				++m_ppHistogramBuffer[pGreen][*pg++];
				++m_ppHistogramBuffer[pBlue][*pb++];
			}
		}
		break;
	default:
		m_ChannelCount = 0;
		// unsupported color mode
		return;
	}

	// evaluate new max value
	m_CurrentMax = m_ppHistogramBuffer[0][0];
	for( int j=0; j<pIB->iChannelCount; j++ )
	{
		for( unsigned int i=1; i<m_valCount; i++ )
		{
			if( m_ppHistogramBuffer[j][i] > m_CurrentMax )
			{
				m_CurrentMax = m_ppHistogramBuffer[j][i];
			}
		}
	}
	if( m_CurrentMax == 0 )
	{
		m_CurrentMax = 1;
	}
	Refresh( boImageFormatChanged );
}

