/***************************************************************************
                          videospotthread.cpp  -  description
                             -------------------
    begin                : Sat Aug 30 2003
    copyright            : (C) 2003 by Martin Merck
    email                : merck@astro.uni-wuerzburg.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "amcframegrabber.h"
#include "videospotthread.h"
#include "videospot.h"
#include "threadevent.h"
#include <math.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qrect.h>
#include <qpoint.h>
#include <qlist.h>

VideoSpotThread::VideoSpotThread( QObject* p_pReceiver, QImage* p_pImage, uchar* p_pDark )
  : m_pReceiver(p_pReceiver), m_pImage(p_pImage), m_pDark(p_pDark)
{
	m_pData = p_pImage->bits();
	m_iScaleMax = 200;
	m_zDarks = false;
	m_zScale = false;
	m_iSigmas = 15;
	m_zIsActive = false;
	m_zStop = false;

	m_qlSpotList.setAutoDelete( true );
}

VideoSpotThread::~VideoSpotThread()
{
	m_qlSpotList.clear();
}

/** No descriptions */
void VideoSpotThread::run()
{
  qDebug("VideoSpotThread PID: %d - PPID: %d", getpid(), getppid() );
	m_zIsActive = false;
	m_zStop = false;

	while( true )
	{
		/*
		 * Check our protected state variable to see if we should stop running
		 */
		m_aMutex.lock();
		if( m_zStop )
		{
		  m_zIsActive = false;
			m_aMutex.unlock();
			return;
		}	

		/*
		 * We wait here to be woken up to process the next frame.
		 * This ensures that when we stop the thread and wan't do delete this class
		 * we get woken up for sure.
		 * Otherwise it could be that when the controlling thread sets the stop varible
		 * and tries to wake us up to finish this loop, we would still process a frame
		 * and will not get woken up. In this case the program would hang.
		 */
		m_aWaitCondition.wait( &m_aMutex );
		if( m_zStop )
		{
//			qDebug("Preparing to exit: Unlocking Mutex");
		  m_zIsActive = false;
			m_aMutex.unlock();
//			qDebug("Stoping  running Thread");
			return;
		}	
 		m_aMutex.unlock();

	  findSpot();
		QThread::postEvent( m_pReceiver, new QCustomEvent( THREAD_SPOT_EVENT ) );

		m_aMutex.lock();
		m_zIsActive = false;
		m_aMutex.unlock();
	}
}

/** Find the Laser spot */
void VideoSpotThread::findSpot()
{

	if( m_zDarks )
		subtractDarks();

	// Rescale image to maximum
	if( m_zScale )
		rescaleImage();

  int threshold = getThreshold();

	m_qlSpotList.clear();
	int top = 0;
	int bottom = MY_HEIGHT;
	int left = 0;
	int right = MY_WIDTH;
	if( ! m_qrROI.isEmpty() )
	{
		top    = m_qrROI.top();
		bottom = m_qrROI.bottom();
		left   = m_qrROI.left();
		right  = m_qrROI.right();
	}

  for (int i=top; i <bottom; i++)
	{
     for (int j=left; j < right ; j++)
		{
			int index= (j + i * MY_WIDTH) * MY_DEPTH;
			bool zFound = false;
			uchar data = m_pData[index];
			if( data > threshold )
	 		{
				for( VideoSpot* pActualSpot = m_qlSpotList.first();
		 				 pActualSpot; pActualSpot = m_qlSpotList.next() )
				{
					if( pActualSpot->contains( j, i ) )
					{
						pActualSpot->append( j, i, data );
						zFound = true;
						break;
					}
				}
				if( zFound == false )
				{
					VideoSpot* pSpot = new VideoSpot();
					pSpot->append( j, i, data );
					m_qlSpotList.append( pSpot );
				}

	 		}
		}
	}
}

/** No descriptions */
void VideoSpotThread::subtractDarks()
{
	uchar* pData = m_pData;
	uchar* pDark = m_pDark;
	uchar* pEnd = pData + ( MY_HEIGHT * MY_WIDTH * MY_DEPTH);
	for( ; pData < pEnd; pData++, pDark++ )
	{
		int help = (int) *pData - (int) *pDark;
		if(help < 0)
			help = 0;
		*pData = (uchar) help;
	}
}

/** No descriptions */
void VideoSpotThread::rescaleImage()
{
//	qDebug("Rescaling image to %d",m_iScaleMax);
	uchar* pData = m_pData;
//	uchar* pEnd = pData + ( MY_HEIGHT * MY_WIDTH * MY_DEPTH);
//	for( ; pData < pEnd; pData++)
	
	long dScale = (255 * 256) / m_iScaleMax;
	for( int ind = 0; ind < MY_HEIGHT * MY_WIDTH * MY_DEPTH; ind++,pData++)
	{
		long help = ( ( ((long) *pData) ) * dScale ) >> 8;
		if( help > 255)
			help = 255;
		*pData = (uchar) help;
	}
}

/** No descriptions */
int VideoSpotThread::getThreshold()
{
	double sumx =0;
	double sumx2 = 0;

	long numPixel = MY_HEIGHT * MY_WIDTH;
	for(int ind=0; ind < ( numPixel * MY_DEPTH); ind+=MY_DEPTH)
	{
		double data = (double) m_pData[ind];
		sumx += data;
		sumx2 += data * data;
	}

	double mean = ((double) sumx) / ((double) (numPixel));
	double var = ((double) sumx2) / ((double) (numPixel)) - mean * mean;
	double stddev = sqrt(var);

  int threshold = (int) (mean + m_iSigmas * stddev);
//	qDebug("Mean: %f StdDev: %f   Threshold %d", mean, stddev, threshold );
	return threshold;
}

void VideoSpotThread::stop()
{
//	qDebug("Setting stop flag");
	m_aMutex.lock();
	m_zStop = true;
	m_aMutex.unlock();
}

bool VideoSpotThread::isActive()
{
	m_aMutex.lock();
	bool zIsActive = m_zIsActive;
	m_aMutex.unlock();

	return zIsActive;                                                                                  		
}

void VideoSpotThread::wake()
{
//	qDebug("Waking up the spot thread");
	if( running() )
	{
		while( true )
		{
			m_aMutex.lock();
			if( ! m_zIsActive )
				break;
			m_aMutex.unlock();
		}		
//		qDebug("Setting active");
 		m_zIsActive = true;
		m_aMutex.unlock();

		m_aWaitCondition.wakeOne();
	}

}

void VideoSpotThread::getSpotList( QList<VideoSpot>* p_pSpots )
{
	
	while( true )
	{
		m_aMutex.lock();
		if( ! m_zIsActive )
			break;
		m_aMutex.unlock();
	}		
	p_pSpots->clear();
	for( VideoSpot* pActualSpot = m_qlSpotList.first();
	  	 pActualSpot; pActualSpot = m_qlSpotList.next() )
	{
		p_pSpots->append( new VideoSpot( *pActualSpot ) );
	}
	m_aMutex.unlock();
	return;
};

void VideoSpotThread::getFrame( uchar* p_pData )
{

	while( true )
	{
		m_aMutex.lock();
		if( ! m_zIsActive )
			break;
		m_aMutex.unlock();
 }		

	m_aDataMutex.lock();
	memcpy( p_pData, m_pData, m_pImage->numBytes());
	m_aDataMutex.unlock();
	m_aMutex.unlock();
}
                      	