#include "AnalysisDataGridTable.h"
#include "VectorScopeCanvas.h"
#include "PropViewFrame.h"
#include <apps/Common/wxAbstraction.h>
#include <wx/dcbuffer.h>
#include <wx/grid.h>
#include <wx/settings.h>

using namespace mvIMPACT::acquire;

//#define TEST_VECTOR_SCOPE
#ifdef TEST_VECTOR_SCOPE
#	define VECTOR_SCOPE_TEST_VALUE_COUNT 1024
#endif // #ifdef TEST_VECTOR_SCOPE

BEGIN_EVENT_TABLE(VectorScopeCanvas, ImageAnalysisPlotCanvas)
  EVT_PAINT(VectorScopeCanvas::OnPaint)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
VectorScopeCanvas::VectorScopeCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = wxT("histogram") */, bool boActive /* = false */ ) :
	ImageAnalysisPlotCanvas(parent, wxT("VectorScope"), id, pos, size, style, name, boActive, 1),
	m_pDataBuffer(0), m_MaxPixelValue(VAL_COUNT_8_BIT)
//-----------------------------------------------------------------------------
{
	InitReferencePoints( 0 );
}
//-----------------------------------------------------------------------------
VectorScopeCanvas::~VectorScopeCanvas()
//-----------------------------------------------------------------------------
{
	DeallocateDataBuffer();
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::CreateGridCustom( void )
//-----------------------------------------------------------------------------
{
	m_pNumericalDisplay->SetTable( new AnalysisDataGridTable(this, m_valCount, 2), true );
	m_pNumericalDisplay->SetCellBackgroundColour( 0, pCb, *wxBLUE );
	m_pNumericalDisplay->SetCellTextColour( 0, pCb, *wxWHITE );
	m_pNumericalDisplay->SetCellBackgroundColour( 0, pCr, *wxRED );
	m_pNumericalDisplay->SetCellTextColour( 0, pCr, *wxWHITE );
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::DeallocateDataBuffer( void )
//-----------------------------------------------------------------------------
{
	delete [] m_pDataBuffer;
	m_pDataBuffer = 0;
}

//-----------------------------------------------------------------------------
wxString VectorScopeCanvas::GetGridValue( int row, int col ) const
//-----------------------------------------------------------------------------
{
	if( ( row >= 0 ) && ( col >= 0 ) )
	{
		if( row == 0 )
		{
			switch( col )
			{
			case 0:
				return wxString( wxT("Cb") );
			case 1:
				return wxString( wxT("Cr") );
			default:
				wxASSERT_MSG( false, wxT("Invalid colum count") );
				break;
			}
		}
		else if( m_pDataBuffer && ( static_cast<unsigned int>( row - 1 ) < m_valCount ) )
		{
			switch( col )
			{
			case 0:
				return wxString::Format( wxT("%.3f"), m_pDataBuffer[row-1].Cb_ );
			case 1:
				return wxString::Format( wxT("%.3f"), m_pDataBuffer[row-1].Cr_ );
			default:
				wxASSERT_MSG( false, wxT("Invalid colum count") );
				break;
			}
		}
	}
	return wxEmptyString;
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::InitReferencePoints( int bitShift )
//-----------------------------------------------------------------------------
{
	m_ReferencePoints[rcRed    ].Set( 255 << bitShift,   0            ,   0            , bitShift );
	m_ReferencePoints[rcYellow ].Set( 255 << bitShift, 255 << bitShift,   0            , bitShift );
	m_ReferencePoints[rcGreen  ].Set(   0            , 255 << bitShift,   0            , bitShift );
	m_ReferencePoints[rcCyan   ].Set(   0            , 255 << bitShift, 255 << bitShift, bitShift );
	m_ReferencePoints[rcBlue   ].Set(   0            ,   0            , 255 << bitShift, bitShift );
	m_ReferencePoints[rcMagenta].Set( 255 << bitShift,   0            , 255 << bitShift, bitShift );
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::PrepareDataBuffer( bool boAOIChanged )
//-----------------------------------------------------------------------------
{
	unsigned int newValCount = ( ( m_AOIw + m_GridStepsX - 1 ) / m_GridStepsX ) * ( ( m_AOIh + m_GridStepsY - 1 ) / m_GridStepsY );
	if( boAOIChanged || !m_pDataBuffer || ( newValCount != m_valCount ) )
	{
		DeallocateDataBuffer();
#ifndef TEST_VECTOR_SCOPE
		m_valCount = newValCount;
#else
		m_valCount = VECTOR_SCOPE_TEST_VALUE_COUNT;
#endif // #ifndef TEST_VECTOR_SCOPE
		m_pDataBuffer = new DataPoint[m_valCount];
	}
}

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

	if( !IsActive() || !m_pDataBuffer )
	{
		return;
	}

	wxCoord w, h;
	dc.GetSize(&w, &h);
	int borderWidth = GetBorderWidth();
	const int RADIUS = ( ( ( w < h ) ? w : h ) - borderWidth ) / 2;
	const double scaleFactor = static_cast<double>(2*RADIUS) / ( 1.0554 * static_cast<double>(m_MaxPixelValue) );
	const wxPoint CENTER(w/2, h/2);

	if( m_boUnsupportedPixelFormat )
	{
		dc.SetBrush( *wxBLACK_BRUSH );
		dc.DrawRectangle( 0, 0, w, h );
		dc.SetTextForeground( *wxRED );
		dc.DrawText( wxString(wxT("Unsupported pixel format")), CENTER.x, CENTER.y );
	}
	else
	{
		wxImage pixelCloud( w, h );
		for( unsigned int i=0; i<m_valCount; i++ )
		{
			pixelCloud.SetRGB( CENTER.x + static_cast<int>(m_pDataBuffer[i].Cb_ * scaleFactor),
				CENTER.y - static_cast<int>(m_pDataBuffer[i].Cr_ * scaleFactor),
				m_pDataBuffer[i].colour_.Red(),
				m_pDataBuffer[i].colour_.Green(),
				m_pDataBuffer[i].colour_.Blue() );
		}
		wxMemoryDC memDC;
		wxBitmap pixelBitmap( pixelCloud, -1 );
		memDC.SelectObject( pixelBitmap );
		dc.Blit( 0, 0, w, h, &memDC, 0, 0 );
	}

	dc.SetBrush( *wxTRANSPARENT_BRUSH );
	const wxCoord rectSize(6);
	for( int i=0; i<rcLAST; i++ )
	{
		dc.SetPen( wxColour(m_ReferencePoints[i].colour_) );
		int xOffsetFromCenter = static_cast<int>(m_ReferencePoints[i].Cb_ * scaleFactor);
		int yOffsetFromCenter = static_cast<int>(m_ReferencePoints[i].Cr_ * scaleFactor);
		dc.DrawRectangle( CENTER.x + xOffsetFromCenter - ( rectSize / 2 ),
			CENTER.y - yOffsetFromCenter - ( rectSize / 2 ),
			rectSize,
			rectSize );
	}
	dc.SetPen( wxColour(128, 128, 128) );
	dc.DrawCircle( CENTER, RADIUS );
	dc.DrawCircle( CENTER, 3 * RADIUS / 4 );
	dc.DrawCircle( CENTER, RADIUS / 2 );
	dc.DrawCircle( CENTER, RADIUS / 4 );
	dc.SetBrush( wxNullBrush );
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::RefreshData( const RequestData& data, 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;
	const ImageBuffer* pIB = data.image_.getBuffer();
	if( !MustUpdate( pIB, pIB->iChannelCount, x, y, w, h, boForceRefresh, &boAOIChanged, &boImageFormatChanged ) )
	{
		return;
	}

	m_ChannelCount = pIB->iChannelCount;
	PrepareDataBuffer( boAOIChanged );
	DataPoint* p = m_pDataBuffer;
	int bitShift = 0;

#ifndef TEST_VECTOR_SCOPE
	switch( pIB->pixelFormat )
	{
	case ibpfMono10:
	case ibpfRGB101010Packed:
		m_MaxPixelValue = VAL_COUNT_10_BIT;
		bitShift = 2;
		break;
	case ibpfMono12:
	case ibpfRGB121212Packed:
		m_MaxPixelValue = VAL_COUNT_12_BIT;
		bitShift = 4;
		break;
	case ibpfMono14:
	case ibpfRGB141414Packed:
		m_MaxPixelValue = VAL_COUNT_14_BIT;
		bitShift = 6;
		break;
	case ibpfMono16:
	case ibpfRGB161616Packed:
		m_MaxPixelValue = VAL_COUNT_16_BIT;
		bitShift = 8;
		break;
	case ibpfMono8:
	case ibpfRGBx888Packed:
	case ibpfRGB888Packed:
	case ibpfRGBx888Planar:
	case ibpfYUV422Packed:
	case ibpfYUV422_UYVYPacked:
	case ibpfYUV422Planar:
	default:
		m_MaxPixelValue = VAL_COUNT_8_BIT;
		break;
	}

	InitReferencePoints( bitShift );

	switch( pIB->pixelFormat )
	{
	case ibpfMono8:
		// no color information!!!
		for( int line=0; line<m_AOIh; line+=m_GridStepsY )
		{
			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_GridStepsX )
			{
				p->Set( *pData, *pData, *pData, bitShift );
			}
		}
		break;
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono14:
	case ibpfMono16:
		// no color information!!!
		for( int line=0; line<m_AOIh; line+=m_GridStepsY )
		{
			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_GridStepsX )
			{
				p->Set( *pData, *pData, *pData, bitShift );
			}
		}
		break;
	case ibpfRGBx888Packed:
	case ibpfRGB888Packed:
		for( int line=0; line<m_AOIh; line+=m_GridStepsY )
		{
			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_GridStepsX )
			{
				p->Set( pData[2], pData[1], pData[0], bitShift );
				++p;
				pData += pIB->iBytesPerPixel*m_GridStepsX;
			}
		}
		break;
	case ibpfRGB101010Packed:
	case ibpfRGB121212Packed:
	case ibpfRGB141414Packed:
	case ibpfRGB161616Packed:
		for( int line=0; line<m_AOIh; line+=m_GridStepsY )
		{
			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_GridStepsX )
			{
				unsigned short r = saveAssign( pData[2], static_cast<unsigned short>(0), static_cast<unsigned short>(m_MaxPixelValue-1) );
				unsigned short g = saveAssign( pData[1], static_cast<unsigned short>(0), static_cast<unsigned short>(m_MaxPixelValue-1) );
				unsigned short b = saveAssign( pData[0], static_cast<unsigned short>(0), static_cast<unsigned short>(m_MaxPixelValue-1) );
				p->Set( r, g, b, bitShift );
				++p;
				pData += 3*m_GridStepsX;
			}
		}
		break;
	case ibpfRGBx888Planar:
		for( int line=0; line<m_AOIh; line+=m_GridStepsY )
		{
			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_GridStepsX )
			{
				p->Set( *pr, *pg, *pb );
				++p;
				pr += m_GridStepsX;
				pg += m_GridStepsX;
				pb += m_GridStepsX;
			}
		}
		break;
	case ibpfYUV422Packed:
	case ibpfYUV422_UYVYPacked:
	case ibpfYUV422Planar:
	default:
		m_boUnsupportedPixelFormat = true;
		break;
	}
#else
	for( int i=0; i<=8; i++ )
	{
		for( int j=0; j<=8; j++ )
		{
			for( int k=0; k<=8; k++ )
			{
				p->Set( saveAssign( 1 << k, 0, 255 ),
					saveAssign( 1 << j, 0, 255 ),
					saveAssign( 1 << i, 0, 255 ) );
				++p;
			}
		}
	}
#endif // #ifndef TEST_VECTOR_SCOPE

	UpdateAnalysisOutput( boAOIChanged );
}

//-----------------------------------------------------------------------------
void VectorScopeCanvas::UpdateGrid( void )
//-----------------------------------------------------------------------------
{
	if( m_pNumericalDisplay )
	{
		unsigned int rowCount = static_cast<unsigned int>(m_pNumericalDisplay->GetTable()->GetNumberRows());
		if( rowCount < ( m_valCount + 1 ) )
		{
			m_pNumericalDisplay->AppendRows( m_valCount + 1 - rowCount );
		}
		else if( rowCount > ( m_valCount + 1 ) )
		{
			m_pNumericalDisplay->DeleteRows( rowCount - 1, rowCount - ( m_valCount + 1 ) );
		}
	}
	ImageAnalysisPlotCanvas::UpdateGrid();
}
