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

IMPLEMENT_DYNAMIC_CLASS(LineProfileCanvas, DrawingCanvas)

BEGIN_EVENT_TABLE(LineProfileCanvas, DrawingCanvas)
  EVT_PAINT(LineProfileCanvas::OnPaint)
  EVT_SIZE(LineProfileCanvas::OnSize)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
LineProfileCanvas::LineProfileCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
				   const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */,
				   const wxString& name /* = "line profile" */, bool boActive /* = false */ )
	: PlotCanvas( parent, id, pos, size, style, name, boActive, 0 ), m_pHorData(0), m_pVerData(0)
//-----------------------------------------------------------------------------
{

}

//-----------------------------------------------------------------------------
LineProfileCanvas::~LineProfileCanvas()
//-----------------------------------------------------------------------------
{
	DeallocateProfileBuffer();
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::DeallocateProfileBuffer( void )
//-----------------------------------------------------------------------------
{
	for( int channel=0; channel<m_ChannelCount; channel++ )
	{
		if( m_pHorData )
		{
			delete [] m_pHorData[channel];
		}
		if( m_pVerData )
		{
			delete [] m_pVerData[channel];
		}
	}
	delete [] m_pHorData;
	delete [] m_pVerData;
	m_pHorData = 0;
	m_pVerData = 0;
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::DrawProfileLine( wxPaintDC& dc, int h, int startOffset, double scaleX, double scaleY, int** pData, int elementCount, TPlane plane )
//-----------------------------------------------------------------------------
{
	dc.SetPen( *m_Pens[plane] );
	int lowerStart = h - GetBorderWidth();
	for( int i=0; i<elementCount-1; i++ )
	{
		dc.DrawLine( static_cast<int>( startOffset + ( i*scaleX ) ),
					 static_cast<int>( lowerStart - ( pData[plane][i] * scaleY ) ),
					 static_cast<int>( startOffset + ( ( i+1 ) * scaleX ) ),
					 static_cast<int>( lowerStart - ( pData[plane][i+1] * scaleY ) ) );
	}
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::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();
	int diagramWidth = static_cast<int>(( w - ( 3 * borderWidth ) + 1.0 ) / 2.0 );
	int diagramHeight = h - ( 2 * borderWidth );
	int verDiagramStart = (2 * borderWidth ) + diagramWidth;
	double scaleY = static_cast<double>(diagramHeight) / static_cast<double>(m_valCount);

	dc.SetPen( *wxTRANSPARENT_PEN );
	dc.SetBrush( *wxWHITE_BRUSH );
	dc.DrawRectangle( borderWidth + 1, borderWidth, diagramWidth + 1, diagramHeight );
	dc.DrawRectangle( verDiagramStart + 1, borderWidth, diagramWidth + 1, diagramHeight );

	// hor. profile
	double scaleX = static_cast<double>(diagramWidth) / static_cast<double>( ( m_AOIw == 0 ) ? 1 : m_AOIw - 1);
	dc.SetPen( *wxBLACK );
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth, borderWidth - 1 );
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth + diagramWidth + 2, h-borderWidth );
	dc.DrawText( wxString::Format( wxT("%d"), m_AOIw ), borderWidth + diagramWidth, h - borderWidth + 3 );
	dc.DrawText( wxString::Format( wxT("%d"), m_valCount - 1 ), borderWidth/2, 1 );
	dc.DrawText( wxT("hor. profile"), diagramWidth/2, 1 );
	if( m_pHorData )
	{
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			DrawProfileLine( dc, h, borderWidth + 1, scaleX, scaleY, m_pHorData, m_AOIw, static_cast<TPlane>(channel) );
		}
	}

	// ver. profile
	scaleX = static_cast<double>(diagramWidth) / static_cast<double>( ( m_AOIh == 0 ) ? 1 : m_AOIh - 1);
	dc.SetPen( *wxBLACK );
	dc.DrawLine( verDiagramStart, h-borderWidth, verDiagramStart, borderWidth - 1 );
	dc.DrawLine( verDiagramStart, h-borderWidth, verDiagramStart + diagramWidth + 2, h-borderWidth );
	dc.DrawText( wxString::Format( wxT("%d"), m_AOIh ), 2*(borderWidth + diagramWidth), h - borderWidth + 3 );
	dc.DrawText( wxString::Format( wxT("%d"), m_valCount - 1 ), static_cast<int>(1.5*static_cast<double>(borderWidth) + diagramWidth + 1), 1 );
	dc.DrawText( wxT("ver. profile"), static_cast<int>(borderWidth + ( 1.5 * static_cast<double>(diagramWidth) )), 1 );
	if( m_pVerData )
	{
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			DrawProfileLine( dc, h, verDiagramStart + 1, scaleX, scaleY, m_pVerData, m_AOIh, static_cast<TPlane>(channel) );
		}
	}
	dc.SetBrush( wxNullBrush );
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::OnSize( wxSizeEvent& event )
//-----------------------------------------------------------------------------
{
	Refresh( true );
	event.Skip();
}

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

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

	if( boAOIChanged || !m_pHorData || !m_pVerData || boImageFormatChanged )
	{
		// free old memory
		DeallocateProfileBuffer();
		// allocate new memory
		m_ChannelCount = pIB->iChannelCount;
		m_pHorData = new int*[pIB->iChannelCount];
		m_pVerData = new int*[pIB->iChannelCount];
		for( int channel=0; channel<pIB->iChannelCount; channel++ )
		{
			m_pHorData[channel] = new int[m_AOIw];
			m_pVerData[channel] = new int[m_AOIh];
		}
	}

	for( int channel=0; channel<pIB->iChannelCount; channel++ )
	{
		memset( m_pHorData[channel], 0, sizeof(int)*m_AOIw);
		memset( m_pVerData[channel], 0, sizeof(int)*m_AOIh);
	}

	// calculate data
	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;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_8_BIT;
		break;
	case ibpfMono10:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_10_BIT;
		break;
	case ibpfMono12:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_12_BIT;
		break;
	case ibpfMono14:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_14_BIT;
		break;
	case ibpfMono16:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_16_BIT;
		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_pHorData[pBlue][pixel] += *pData;
				m_pVerData[pBlue][line] += *pData++;
				m_pHorData[pGreen][pixel] += *pData;
				m_pVerData[pGreen][line] += *pData++;
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
				*pData++;
			}
		}
		m_valCount = VAL_COUNT_8_BIT;
		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_pHorData[pBlue][pixel] += *pData;
				m_pVerData[pBlue][line] += *pData++;
				m_pHorData[pGreen][pixel] += *pData;
				m_pVerData[pGreen][line] += *pData++;
				m_pHorData[pRed][pixel] += *pData;
				m_pVerData[pRed][line] += *pData++;
			}
		}
		m_valCount = VAL_COUNT_8_BIT;
		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_pHorData[pRed][pixel] += *pr;
				m_pVerData[pRed][line] += *pr++;
				m_pHorData[pGreen][pixel] += *pg;
				m_pVerData[pGreen][line] += *pg++;
				m_pHorData[pBlue][pixel] += *pb;
				m_pVerData[pBlue][line] += *pb++;
			}
		}
		m_valCount = VAL_COUNT_8_BIT;
		break;
	default:
		// unsupported colour format
		m_valCount = VAL_COUNT_8_BIT;
		break;
	}

	for( int channel=0; channel<m_ChannelCount; channel++ )
	{
		for( int i=0; i<m_AOIw; i++ )
		{
			m_pHorData[channel][i] /= m_AOIh;
		}
		for( int i=0; i<m_AOIh; i++ )
		{
			m_pVerData[channel][i] /= m_AOIw;
		}
	}

	bool boFullUpdate = boAOIChanged || boImageFormatChanged;
	Refresh(boFullUpdate);
}

