// MotionSensor.cpp : Implementation of CMotionSensor
#include "stdafx.h"
#include "ChannelObjects.h"
#include "MotionSensor.h"

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// CMotionSensor
//\_

STDMETHODIMP CMotionSensor::FinalConstruct ()
{
	HRESULT hr;
	_ComVerifyRS( hr = AddConnector( CLSID_ChannelInputConnector, GUID_VideoDataType, alignLeft ) );
	_VerifyRS( m_oVideoInput = GetConnector( hr ) );
	_ComVerifyRS( hr = AddConnector( CLSID_ChannelOutputConnector, GUID_BooleanDataType, alignTop ) );
	_VerifyRS( m_oSensorOutput = GetConnector( hr ) );
	_ComVerifyRS( hr = AddConnector( CLSID_ChannelOutputConnector, GUID_VideoDataType, alignRight ) );
	_VerifyRS( m_oVideoOutput = GetConnector( hr ) );

	_ComVerifyRS( DoAdvise( m_oVideoInput, IID__IInputConnectorEvents ) )
	_ComVerifyRS( DoAdvise( m_oSensorOutput, IID__IOutputConnectorEvents ) );
	_ComVerifyRS( DoAdvise( m_oVideoOutput, IID__IOutputConnectorEvents ) );

	_ComVerifyRS( DoAdvise( m_oVideoInput, IID__IConnectorEvents ) )
	_ComVerifyRS( DoAdvise( m_oSensorOutput, IID__IConnectorEvents ) );
	_ComVerifyRS( DoAdvise( m_oVideoOutput, IID__IConnectorEvents ) );

	// Creates event handles
	_VerifyRS( m_hStopProcess = CreateEvent ( NULL, TRUE, FALSE, NULL ) );
	_VerifyRS( m_hProcessImage = CreateEvent ( NULL, TRUE, FALSE, NULL ) );
	_VerifyRS( m_hTimerEvent = CreateEvent ( NULL, TRUE, FALSE, NULL ) );
	_VerifyRS( m_hVideoOutReady = CreateEvent ( NULL, TRUE, FALSE, NULL ) );

	return S_OK;
}

STDMETHODIMP CMotionSensor::FinalRelease ()
{
	// Close event handles
	if ( m_hStopProcess )
		::CloseHandle( m_hStopProcess ), m_hStopProcess = NULL;
	if ( m_hProcessImage )
		::CloseHandle( m_hProcessImage ), m_hProcessImage = NULL;
	if ( m_hVideoOutReady )
		::CloseHandle( m_hVideoOutReady ), m_hProcessImage = NULL;

	if (m_nTimerID) timeKillEvent(m_nTimerID);

	if ( m_hTimerEvent )
		::CloseHandle( m_hTimerEvent ), m_hTimerEvent = NULL;

	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// connectors events
//\_

void CMotionSensor::OnAddConnector( IConnector* pIConnector )
{
	_VerifyRV( pIConnector );
	CLSID guidDataType;
	_ComVerifyRV( pIConnector->get_DataType( &guidDataType ) );
	if ( guidDataType == GUID_BooleanDataType )
	{
		CLSID clsid;
		_ComVerifyRV( pIConnector->get_ClassType( &clsid ) );
		if ( clsid == CLSID_ChannelInputConnector )
			_ComVerifyRV( DoAdvise( pIConnector, IID__IInputConnectorEvents ) )
		else if ( clsid == CLSID_ChannelOutputConnector )
			_ComVerifyRV( DoAdvise( pIConnector, IID__IOutputConnectorEvents ) )
		_ComVerifyRV( DoAdvise( pIConnector, IID__IConnectorEvents ) );
	}
}

void CMotionSensor::OnRemoveConnector( IConnector* pIConnector )
{
	_VerifyRV( pIConnector );
	CLSID guidDataType;
	_ComVerifyRV( pIConnector->get_DataType( &guidDataType ) );
	if ( guidDataType == GUID_BooleanDataType )
	{
		CLSID clsid;
		_ComVerifyRV( pIConnector->get_ClassType( &clsid ) );
		if ( clsid == CLSID_ChannelInputConnector )
			DoUnadvise( pIConnector, IID__IInputConnectorEvents );
		else if ( clsid == CLSID_ChannelOutputConnector )
			DoUnadvise( pIConnector, IID__IOutputConnectorEvents );
		DoUnadvise( pIConnector, IID__IConnectorEvents );
	}
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// interface IStructureItem
//\_

STDMETHODIMP CMotionSensor::Init()
{
	if ( !m_aroStates.size() )
	{
		CComPtr<IUnknown> oState;
		_ComVerifyRS( CreateAppObject( CLSID_MotionSensorState, &oState, GetApplication(), (IAppObject*) this ) );
		AddInitialState( oState );
	}

	_ComVerifyRS( CreateAppObject( CLSID_MathFactory, &m_oMath, GetApplication(), (IAppObject*) this ) );

	// start timer, we need frames not so often
	_ComVerifyRS( SetupFPSTimer() );

	StartProcessThread();
	return IStructureItemImpl::Init();
}

STDMETHODIMP CMotionSensor::PostInit()
{
	if ( !m_bsSIName.Length() )
		_ComVerifyRS( LoadString( 1, &m_bsSIName ) );

	_ComCallRH( PostInitChannelObject() );

	_ComVerifyRS( DoAdvise( m_oChannel, IID__IChannelEvents ) );
	_ComVerifyRS( m_oChannel->get_TransferMode( &m_TransferMode ) );

	return IStructureItemImpl::PostInit();
}

STDMETHODIMP_(void) CMotionSensor::PreClose()
{
	DoUnadvise();
	UninitChannelObject();

	IStructureItemImpl::PreClose();

	Lock();
	m_oChannel.Release();
	Unlock();
}

STDMETHODIMP_(void) CMotionSensor::Close()
{
	StopProcessThread();
	CleanStates();
	m_oMath.Release();
	IStructureItemImpl::Close();
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// Threat helpers
//\_
BOOL CMotionSensor::StartProcessThread()
{
	StopProcessThread();
	UINT
		nThreadID = 0;
	::ResetEvent( m_hStopProcess );
	_VerifyRS( m_hProcessThread = (HANDLE) _beginthreadex( NULL, 0, ProcessData, this, 0, &nThreadID ) );

	return m_hProcessThread != NULL;
}

void CMotionSensor::StopProcessThread()
{
	// If thread was started, try to stop it.
	if ( m_hProcessThread )
	{
		::SetEvent( m_hStopProcess );

		DWORD
			dwResult = ::WaitForSingleObject( m_hProcessThread, INFINITE );

		// Terminate thread if it was not finished.
		if ( WAIT_TIMEOUT == dwResult )
			// Manualy terminate thread.
			::TerminateThread( m_hProcessThread, 0 );

		// Close thread handle.
		::CloseHandle( m_hProcessThread );
		m_hProcessThread = NULL;
	}
}

UINT CMotionSensor::ProcessData( void* _pThis )
{
	CMotionSensor*
		pThis = reinterpret_cast<CMotionSensor*>( _pThis );

	CoInitialize( NULL );

	try
	{
		HANDLE
			arhWait[] = { pThis->m_hStopProcess, pThis->m_hProcessImage };

		HANDLE
			arhWait2[] = { pThis->m_hStopProcess, pThis->m_hVideoOutReady };

		// whait for some event
		while ( WAIT_OBJECT_0 != ::WaitForMultipleObjects( 2, arhWait, FALSE, INFINITE ) )
		{
			ResetEvent( pThis->m_hProcessImage );

			pThis->Lock();
			CComQIPtr<IObjectState> oState(pThis->m_oStateActive);
			pThis->Unlock();

			if (oState)
			{
				BOOL bActive; 	_ComVerifyES( oState->get_ObjectActivity( &bActive ) );

				// process data if neded
				if (bActive && (WAIT_OBJECT_0 == WaitForSingleObject(pThis->m_hTimerEvent,0)))
				{
					ResetEvent( pThis->m_hTimerEvent );

					// store data pointer for future processing
					CDataObjects<IImageData>
						arImages( pThis->m_oCurFrame );
					CDataObjects<IMaskerData>
						arMasks( pThis->m_oCurFrame );

					// some processing
					int nPrevSample = pThis->m_nCurSample;
					pThis->m_nCurSample=(pThis->m_nCurSample+1)%2;
					C2DArray<SAMPLEDESC> &oCurSample=pThis->m_arSamples[pThis->m_nCurSample];
					_ComVerifyES(pThis->PreProcessImage(arImages[0],oCurSample));

					// perform compare
					if (nPrevSample!=-1)
					{
						C2DArray<SAMPLEDESC> &oPrevSample=pThis->m_arSamples[nPrevSample];

						CComQIPtr<IMotionSensorProperties> oProperties(oState);

						long nColorSensivity, nBrightnessSensivity;
						_ComVerifyES(oProperties->get_ColorSensivity(&nColorSensivity));
						_ComVerifyES(oProperties->get_BrightnesSensivity(&nBrightnessSensivity));
						nColorSensivity = msb_MaxSensivity-nColorSensivity;
						nBrightnessSensivity = msb_MaxSensivity - nBrightnessSensivity;

						int nWidth = oCurSample.GetWidth();
						int nHeight = oCurSample.GetHeight();
						int nDiffSamples=0,nTotalSamples=0;

						// get surveliance areas
						vector < vector< POINT > > arPolyMasks;
						if (arMasks.size()) {
							arPolyMasks.resize(arMasks.size());
							for (int i=0; i<arMasks.size(); i++ )
							{
								long nMaskPointCount;
								_ComVerifyES(arMasks[i]->get_PointsCnt(&nMaskPointCount));
								arPolyMasks[i].resize(nMaskPointCount);
								if (nMaskPointCount)
									arMasks[0]->GetPoints(&nMaskPointCount,&arPolyMasks[i].front());
							}
						}

						// now perform compare
						for (int x=0; x<nWidth; x++)
							for (int y=0; y<nHeight; y++)
							{
								BOOL bNeedAnalyze=TRUE;
								if (arPolyMasks.size())
								{
									bNeedAnalyze=FALSE;
									POINT ptHit; 
									ptHit.x=x*pThis->m_nResample+pThis->m_nResample/2;
									ptHit.y=y*pThis->m_nResample+pThis->m_nResample/2;
									BOOL bInPoly;
									for (int i=0; i< arPolyMasks.size(); i++)
									{
										_ComVerifyES(pThis->m_oMath->PtInPoly(arPolyMasks[i].size(),&arPolyMasks[i].front(),&ptHit,&bInPoly));
										if ( bInPoly )
										{
											bNeedAnalyze=TRUE; break;
										}
									}
								}
								if (bNeedAnalyze)
								{
									SAMPLEDESC prev, cur;
									oPrevSample.Get(x,y,prev);
									oCurSample.Get(x,y,cur);

									BYTE Y1,Y2,U1,U2,V1,V2;

									// convert to YUV
									Y1=(19660*prev.G+9830*prev.R+3277*prev.B)>>15;
									U1=prev.B-Y1;
									V1=prev.R-Y1;

									Y2=(19660*cur.G+9830*cur.R+3277*cur.B)>>15;
									U2=prev.B-Y2;
									V2=prev.R-Y2;

									// compare
									if (abs(Y1-Y2)>nBrightnessSensivity
										|| abs(U1-U2)>nColorSensivity 
										|| abs(V1-V2)>nColorSensivity
										)
										nDiffSamples++;

									nTotalSamples++;
								}
							}

						// analyze results
						double C=double(nDiffSamples)/nTotalSamples;

						BOOL bStateChanged=FALSE;

						long nSensivity, nRateLag;
						_ComVerifyES(oProperties->get_Sensivity(&nSensivity));
						_ComVerifyES(oProperties->get_ResponceDelay(&nRateLag));

						double MinRatio =0.02+0.14* (double(msb_MaxSensivity-nSensivity)/msb_MaxSensivity);

						// check need or not to change state
						if (C>MinRatio)
						{
							if (nRateLag>=pThis->m_nAccMoving)
							{
								if (!pThis->m_bMotion)
								{
									pThis->m_bMotion = TRUE;
									bStateChanged = TRUE;
								}
							}
							else pThis->m_nAccMoving++;
						}
						else
						{
							if (pThis->m_nAccMoving==0)
							{
								if (pThis->m_bMotion)
								{
									pThis->m_bMotion = FALSE;
									bStateChanged = TRUE;
								}
							}
							else pThis->m_nAccMoving--;
						}

						// trye to imedially send result					
						if (bStateChanged && pThis->m_oSensorOutput->IsReady()==S_OK)
						{
							CComPtr<IBooleanData> oData;
							_ComVerifyES( CreateAppObject( CLSID_BooleanData, &oData, pThis->GetApplication(), (IAppObject*) pThis ) );
							oData->put_Value( pThis->m_bMotion );
							pThis->m_oSensorOutput->put_Data( oData );
						}
					}
				}
			}

			// whait while we can send data
			if ( WAIT_OBJECT_0 != ::WaitForMultipleObjects( 2, arhWait2, FALSE, INFINITE ) )
			{
				// send data
				pThis->m_oVideoOutput->put_Data( pThis->m_oCurFrame );

				// get ready for new data
				if (pThis->m_TransferMode == ctmParallel) 
					pThis->m_oVideoInput->put_Ready(TRUE);
			};
		}
	} catch( HRESULT hr )
	{
		if ( FAILED( hr ) ) ::MessageBox( NULL, GetErrorDescription().c_str(), "Error", MB_OK|MB_ICONEXCLAMATION );
	}

	CoUninitialize();

	return 0;
}

HRESULT CMotionSensor::PreProcessImage( CComPtr<IImageData> oImageData, C2DArray<SAMPLEDESC> &arSamples )
{
	_VerifyRS( oImageData );

	long
		nImageWidth,
		nImageHeight,
		nBpp;

	_ComVerifyRS( oImageData->get_Width( &nImageWidth ) );
	_ComVerifyRS( oImageData->get_Height( &nImageHeight ) );
	_ComVerifyRS( oImageData->get_BPP( &nBpp ) );

	Lock();
	CComQIPtr<IMotionSensorProperties> oProperties(m_oStateActive);
	Unlock();

	long nResolution;
	_ComVerifyRS(oProperties->get_Sensivity(&nResolution));
	nResolution = 16+ 32*(double(nResolution)/msb_MaxSensivity);

	int nBpLine=nImageWidth*nBpp/8;
	m_nResample = nImageWidth/nResolution;

	arSamples.NewSize(nImageWidth/m_nResample+(nImageWidth%m_nResample?1:0),
			nImageHeight/m_nResample+(nImageHeight%m_nResample?1:0));

	BYTE *pByteBuf;
	_ComVerifyRS( oImageData->LockBuffer( (void**) &pByteBuf ) );

	for (int y=0,nSampleY=0;y<nImageHeight;y+=m_nResample,nSampleY++)
	{
		for (int x=0, nSampleX=0;x<nImageWidth;x+=m_nResample,nSampleX++)
		{
			int nR=0, nG=0, nB=0, nCount=0;
			for (int y1=y; y1<y+m_nResample && y1<nImageHeight; y1++)
				for (int x1=x; x1<x+m_nResample && x1<nImageWidth; x1++)
				{
					BYTE *pPixel = pByteBuf + y1*nBpLine + x1*3;
					nR+=pPixel[0];
					nG+=pPixel[1];
					nB+=pPixel[2];
					nCount++;
				}
			if (nCount) {
				SAMPLEDESC sdesc;
				sdesc.R=nR/nCount;
				sdesc.G=nG/nCount;
				sdesc.B=nB/nCount;
				arSamples.Set(nSampleX,nSampleY,sdesc);
			}
		}
	} 

	_ComVerifyRS( oImageData->UnlockBuffer() );

	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// _IInputConnectorEvents events
//\_
STDMETHODIMP_(void) CMotionSensor::OnDataReceived(IInputConnector * pIConnector, IUnknown* pDataUnk)
{
	_VerifyRV(pIConnector);

	GUID guidDataType;
	pIConnector->get_DataType(&guidDataType);

	if ( guidDataType == GUID_BooleanDataType )
	{
		ProcessStateInputConnectorData( pIConnector, pDataUnk );
		return;
	}

	AUTO_LOCKER;

	BOOL bReady, bRequest;
	_VerifyRV(pIConnector);
	pIConnector->get_Ready(&bReady);
	pIConnector->get_Request(&bRequest);

	// if this input not ready simply return with no doubts
	if (!bReady)	return;

	long nID;
	pIConnector->get_ID(&nID);

	// if we are in request mode clear this flag
	if (bRequest) pIConnector->put_Request(FALSE);
	pIConnector->put_Ready(FALSE);

	if (InlineIsEqualGUID(guidDataType,GUID_VideoDataType))
	{
		m_oCurFrame = pDataUnk;
		SetEvent( m_hProcessImage );
	}
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// _IOutputConnectorEvents
//\_
STDMETHODIMP_(void) CMotionSensor::OnStateChanged(/*[in]*/ IOutputConnector* pIConnector )
{
	_VerifyRV( pIConnector );

	if (m_oSensorOutput.IsEqualObject(pIConnector))
	{
		// if data are ready, we need to send it
		if ( pIConnector->IsReady() == S_OK && pIConnector->IsRequest() == S_OK)
		{
			CComPtr<IBooleanData> oData;
			_ComVerifyRV( CreateAppObject( CLSID_BooleanData, &oData, GetApplication(), (IAppObject*) this ) );
			oData->put_Value( m_bMotion );
			pIConnector->put_Data( oData );
		};
	}

	if (m_oVideoOutput.IsEqualObject(pIConnector))
	{
		// we are passive object, retranslate request to previouse object
		if (pIConnector->IsRequest() == S_OK)
			m_oVideoInput->put_Request(TRUE);

		// if data are ready, we need to send it
		if ( pIConnector->IsReady() == S_OK )
		{
			SetEvent( m_hVideoOutReady );
			if (m_TransferMode == ctmWaitComplete) 
				m_oVideoInput->put_Ready(TRUE);
		}
		else
			ResetEvent ( m_hVideoOutReady );
	}
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// _IConnectorEvents
//\_
STDMETHODIMP_(VOID) CMotionSensor::OnConnected(IConnector * pIConnector)
{
	if ( pIConnector->IsConnectorInput() == S_OK )
	{
		CComQIPtr<IInputConnector>
			oInputConnector = pIConnector;
		_VerifyRV( oInputConnector );
		oInputConnector->put_Ready(TRUE);
		oInputConnector->put_Request(TRUE);
	}
	
	if (m_oVideoOutput.IsEqualObject(pIConnector))
	{
		if ( m_oVideoOutput->IsReady() == S_OK )
			SetEvent( m_hVideoOutReady );
		else
			ResetEvent ( m_hVideoOutReady );
	}
}

STDMETHODIMP_(VOID) CMotionSensor::OnDisconnected(IConnector * pIConnector)
{
	if ( m_oVideoOutput.IsEqualObject(pIConnector) )
		ResetEvent ( m_hVideoOutReady );
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// interface _IChannelEvents
//\_
STDMETHODIMP_(void) CMotionSensor::OnTransferModeChanged( CHANNEL_MODE newMode )
{
	AUTO_LOCKER;
	m_TransferMode = newMode;
	return;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// interface _IMotionSensorStateEvents
//\_
STDMETHODIMP_(void) CMotionSensor::OnDetectionRateChanged(IMotionSensorProperties * pState)
{
	_VerifyRV( pState );

	_ComVerifyRV( pState->get_MaxDetectionRate(&m_nFPS) );
	SetupFPSTimer();
	return;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// interface IPersistStream
//\_
STDMETHODIMP CMotionSensor::Save(IStream *pStm, BOOL fClearDirty)
{
	_ComCallRH( SaveSignature( pStm, "MOTIONSENSOR" ) );
	_ComCallRH( SaveVersion<short>( pStm, 1 ) );
	_ComVerifyRS( SISave( pStm ) );
	_ComVerifyRS( IChannelObjectImpl::Save( pStm ) );
	_ComCallRH( m_bsSIName.WriteToStream( pStm ) );
	_ComCallRH( m_bsSIDescr.WriteToStream( pStm ) );
	_ComCallRH( IChannelObjectImpl::SaveStates( pStm ) );
	return S_OK;
}

STDMETHODIMP CMotionSensor::Load(IStream *pStm)
{
	try
	{
		_VerifyRS( CheckSignature( pStm, "MOTIONSENSOR" ) );

		switch ( LoadVersion<short>( pStm ) )
		{
			case 0:
				return LoadVersion0( pStm );
			case 1:
				return LoadVersion1( pStm );

		}
	}
	catch ( HRESULT hr )
	{
		return hr;
	}

	_ComVerifyRS( E_UNEXPECTED );
}

STDMETHODIMP CMotionSensor::LoadVersion0(IStream *pStm)
{
	_ComVerifyRS( SILoad( pStm ) );
	_ComVerifyRS( IChannelObjectImpl::Load( pStm ) );

	CComBSTR
		bsSIName;

	_ComCallRH( bsSIName.ReadFromStream( pStm ) );
	_ComVerifyRS( put_SIName( bsSIName ) );

	return S_OK;
}

STDMETHODIMP CMotionSensor::LoadVersion1(IStream *pStm)
{
	_ComVerifyRS( SILoad( pStm ) );
	_ComVerifyRS( IChannelObjectImpl::Load( pStm ) );

	CComBSTR
		bsSIName,
		bsSIDescr;

	_ComCallRH( bsSIName.ReadFromStream( pStm ) );
	_ComCallRH( bsSIDescr.ReadFromStream( pStm ) );

	_ComVerifyRS( put_SIName( bsSIName ) );
	_ComVerifyRS( put_SIDescription( bsSIDescr ) );

	_ComCallRH( IChannelObjectImpl::LoadStates( pStm ) );

	return S_OK;
}


//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// ICloneObject
//\_
STDMETHODIMP CMotionSensor::Assign( IUnknown* pSourceObj )
{
	if( pSourceObj == NULL ) return E_POINTER;

	CComQIPtr< ICloneObject > 
		oCloneObject( pSourceObj );

	if( oCloneObject==NULL ) return E_NOINTERFACE;

	// check clone id

	CLSID 
		clsid;
	_ComVerifyRS( oCloneObject->get_ObjectCLSID(&clsid) );

	_VerifyRS( InlineIsEqualGUID(clsid,GetObjectCLSID()) );
	_ComCallRH( AssignPart( IID_IStructureItem, pSourceObj) );
	_ComCallRH( AssignPart( IID_IGraphicsChannelObject, pSourceObj ) );
	_ComCallRH( AssignPart( IID_IMultiStateManager, pSourceObj ) );

	return S_OK;
}

STDMETHODIMP CMotionSensor::AssignPart( REFIID riid, IUnknown *pSource )
{
	if ( riid == IID_IStructureItem )
		return AssignIStructureItem( pSource );

	if ( riid == IID_IGraphicsChannelObject )
		return AssignIGraphicsChannelObject( pSource );

	if ( riid == IID_IMultiStateManager )
		return AssignIMultiStateManager( pSource );

	return E_FAIL;
}

STDMETHODIMP CMotionSensor::EraseCloneHistory()
{
	_ComCallRH( ICloneObjectImpl::EraseCloneHistory() );
	EraseStatesCloneHistory();
	return S_OK;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// _IMultiStateEvents
//\_
void CMotionSensor::_OnActiveStateChanged( IUnknown* pStateUnk )
{
	DoUnadvise( IID__IMotionSensorStateEvents );
	_ComVerifyRV( DoAdvise( pStateUnk, IID__IMotionSensorStateEvents ) );

	CComQIPtr<IMotionSensorProperties> oSettings = pStateUnk;
	_VerifyRV( oSettings );

	// get on the fly data
	_ComVerifyRV( oSettings->get_MaxDetectionRate(&m_nFPS) );

	SetupFPSTimer();
}

//\/\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// IChannelMenuHandler interface
//\_

STDMETHODIMP CMotionSensor::ProcessChannelEditorContextMenu( IN IVssContextMenu* pIVssContextMenu )
{
	return AddStatesToContextMenu( pIVssContextMenu, this );
}

//\/\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// IChannelMenuHandler interface
//\_

STDMETHODIMP CMotionSensor::ExecuteCommand( UINT nID )
{
	return ShowProperties4State( nID );
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// helper functions
//\_
HRESULT CMotionSensor::SetupFPSTimer()
{
	if (m_nTimerID) timeKillEvent(m_nTimerID);

	double timer=m_nFPS>1?(1/double(m_nFPS))*1000:1000;

	m_nTimerID = timeSetEvent(timer,0,LPTIMECALLBACK(m_hTimerEvent),NULL,TIME_PERIODIC|TIME_CALLBACK_EVENT_SET);
	
	if (m_nTimerID) return S_OK;
		else return E_FAIL;
}

