//-----------------------------------------------------------------------------
#include <apps/Common/wxAbstraction.h>
#include "CaptureThread.h"
#include "DevCtrl.h"
#include "EventThread.h"
#include "PropTree.h"

using namespace std;
using namespace mvIMPACT::acquire;

//-----------------------------------------------------------------------------
DevicePropertyHandler::DevicePropertyHandler( wxPropertyGrid* pGrid, bool boDisplayDebugInfo, bool boDisplayFullTree, bool boDisplayInvisibleComponents, bool boShowEvents ) :
	m_actDevListChangedCounter(0), m_boDisplayDebugInfo(boDisplayDebugInfo), m_boDisplayInvisibleComponents(boDisplayInvisibleComponents),
	m_boShowEvents(boShowEvents), m_fullTreeChangedCounter(0), m_pFullTree(0), m_pActiveDevData(0), m_pActiveDevice(0),
	m_pPropertyGrid(pGrid), m_viewMode(vmStandard), m_boUseHexIndices(false)
//-----------------------------------------------------------------------------
{
	if( boDisplayFullTree )
	{
		m_pFullTree = new PropTree( ROOT_LIST, "Full Tree", pGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
		m_pFullTree->Draw();
	}
	else
	{
		PrepareTreeArray();
	}
}

//-----------------------------------------------------------------------------
DevicePropertyHandler::~DevicePropertyHandler()
//-----------------------------------------------------------------------------
{
	m_pPropertyGrid->Freeze();
	DevToTreeMap::iterator itStart = m_devToTreeMap.begin();
	DevToTreeMap::iterator itEnd = m_devToTreeMap.end();
	while( itStart != itEnd )
	{
		CloseDriver( ConvertedString(itStart->first->serial.read()) );
		if( itStart->second->pCaptureThread )
		{
			itStart->second->pCaptureThread->Delete();
			delete itStart->second->pCaptureThread;
			itStart->second->pCaptureThread = 0;
		}
		if( itStart->second->pEventThread )
		{
			itStart->second->pEventThread->Delete();
			delete itStart->second->pEventThread;
			itStart->second->pEventThread = 0;
		}
		DeleteDriverTrees( itStart );
		delete itStart->second->pDeviceTree;
		delete itStart->second->pEventSubSubsystem;
		delete itStart->second->pFuncInterface;
		delete itStart->second->pStatistics;
		delete itStart->second;
		++itStart;
	}
	delete m_pFullTree;
	m_pPropertyGrid->Thaw();
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::CloseDriver( const wxString& name )
//-----------------------------------------------------------------------------
{
	string serialANSI(name.mb_str());
	Device* pDev = m_devMgr.getDeviceBySerial( serialANSI );
	if( pDev && pDev->isOpen() )
	{
		DevToTreeMap::iterator it = m_devToTreeMap.find( pDev );
		if( it != m_devToTreeMap.end() )
		{
			wxCriticalSectionLocker locker(m_critSect);
			it->second->pCaptureThread->SetLiveMode( false );
			if( it->second->pCaptureThread )
			{
				it->second->pCaptureThread->Delete();
				delete it->second->pCaptureThread;
				it->second->pCaptureThread = 0;
			}
			it->second->boWasLive = false;
			if( it->second->pEventThread )
			{
				it->second->pEventThread->Delete();
				delete it->second->pEventThread;
				it->second->pEventThread = 0;
			}
			m_pPropertyGrid->Freeze();
			DeleteDriverTrees( it );
			m_pPropertyGrid->Thaw();
			delete it->second->pEventSubSubsystem;
			it->second->pEventSubSubsystem = 0;
			delete it->second->pFuncInterface;
			it->second->pFuncInterface = 0;
			delete it->second->pStatistics;
			it->second->pStatistics = 0;
		}
		pDev->close();
	}

	if( m_pFullTree )
	{
		delete m_pFullTree;
		// there has been one before, so there should be one now! WORK AROUND!!!
		m_pFullTree = new PropTree( ROOT_LIST, "Full Tree", m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
		m_pFullTree->Draw();
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::CreateDriverTrees( DevToTreeMap::iterator& it )
//-----------------------------------------------------------------------------
{
	if( !it->first->isOpen() )
	{
		return;
	}

	ComponentLocator cl(it->first->hDrv());
	DeviceComponentLocator dcl(it->first->hDrv());
	for( unsigned int i=0; i<m_vTreeProps.size(); i++ )
	{
		try
		{
			PropTree* pTree = 0;
#ifdef BUILD_WITH_PLUGIN_SUPPORT
			if( m_vTreeProps[i].ListName == "Plugins" )
			{
				ComponentLocator tmpCL(0);
				pTree = new PropTree( tmpCL.findComponent( m_vTreeProps[i].ListName.c_str(), smIgnoreMethods | smIgnoreProperties ), m_vTreeProps[i].DisplayName.c_str(), m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
			} else
#endif // #ifdef BUILD_WITH_PLUGIN_SUPPORT
			if( m_vTreeProps[i].ListType == dltUndefined )
			{
				pTree = new PropTree( cl.findComponent( m_vTreeProps[i].ListName.c_str(), smIgnoreMethods | smIgnoreProperties ), m_vTreeProps[i].DisplayName.c_str(), m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
			}
			else
			{
				pTree = new PropTree( dcl.bindSearchBaseList( it->first, m_vTreeProps[i].ListType ), m_vTreeProps[i].DisplayName.c_str(), m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
			}
			it->second->vDriverTrees.push_back( pTree );
			pTree->Draw();
		}
		catch( const ImpactAcquireException& )
		{
			// this list is not supported by this device
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::DeleteDriverTrees( DevToTreeMap::iterator& it )
//-----------------------------------------------------------------------------
{
	while( !it->second->vDriverTrees.empty())
	{
		delete it->second->vDriverTrees.back();
		it->second->vDriverTrees.pop_back();
	}
}

//-----------------------------------------------------------------------------
unsigned int DevicePropertyHandler::GetDisplayFlags( void ) const
//-----------------------------------------------------------------------------
{
	EDisplayFlags flags = dfDummy;
	if( m_boDisplayDebugInfo )
	{
		flags = EDisplayFlags(flags | dfDisplayDebugInfo);
	}
	if( m_boDisplayInvisibleComponents )
	{
		flags = EDisplayFlags(flags | dfDisplayInvisibleComponents);
	}
	if( m_boUseHexIndices )
	{
		flags = EDisplayFlags(flags | dfHexIndices);
	}
	return flags;
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::GetInterfaceClasses( const DeviceData* p, FunctionInterface** ppFI /* = 0 */, const Statistics** ppS /* = 0 */, CaptureThread** ppCT /* = 0 */, mvIMPACT::acquire::EventSubSystem** ppESS /* = 0 */ ) const
//-----------------------------------------------------------------------------
{
	if( p )
	{
		if( ppFI )
		{
			*ppFI = p->pFuncInterface;
		}
		if( ppS )
		{
			*ppS = p->pStatistics;
		}
		if( ppCT )
		{
			*ppCT = p->pCaptureThread;
		}
		if( ppESS )
		{
			*ppESS = p->pEventSubSubsystem;
		}
	}
}

//-----------------------------------------------------------------------------
Device* DevicePropertyHandler::GetActiveDevice( FunctionInterface** ppFI /* = 0 */, const Statistics** ppS /* = 0 */, CaptureThread** ppCT /* = 0 */, mvIMPACT::acquire::EventSubSystem** ppESS /* = 0 */ ) const
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	GetInterfaceClasses( m_pActiveDevData, ppFI, ppS, ppCT, ppESS );
	return m_pActiveDevice;
}

//-----------------------------------------------------------------------------
bool DevicePropertyHandler::GetDeviceData( mvIMPACT::acquire::Device* pDev, mvIMPACT::acquire::FunctionInterface** ppFI /* = 0 */, const mvIMPACT::acquire::Statistics** ppS /* = 0 */, CaptureThread** ppCT /* = 0  */, mvIMPACT::acquire::EventSubSystem** ppESS /* = 0 */ ) const
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	DevToTreeMap::const_iterator it = m_devToTreeMap.find( pDev );
	if( it != m_devToTreeMap.end() )
	{
		GetInterfaceClasses( it->second, ppFI, ppS, ppCT, ppESS );
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::OpenDriver( const wxString& name, wxWindow* pParentWindow )
//-----------------------------------------------------------------------------
{
	string serialANSI(name.mb_str());
	Device* pDev = m_devMgr.getDeviceBySerial( serialANSI );
	if( pDev && !pDev->isOpen() )
	{
		UpdateTreeMap();
		pDev->open();
		DevToTreeMap::iterator it = m_devToTreeMap.find( pDev );
		if( it != m_devToTreeMap.end() )
		{
			{
				wxCriticalSectionLocker locker(m_critSect);
				if( !m_pFullTree && ( it->second->vDriverTrees.size() != m_vTreeProps.size() ) )
				{
					CreateDriverTrees( it );
				}

				if( it->second->pEventSubSubsystem == 0 )
				{
					it->second->pEventSubSubsystem = new EventSubSystem(pDev);
				}
				if( it->second->pFuncInterface == 0 )
				{
					it->second->pFuncInterface = new FunctionInterface(pDev);
				}
				if( it->second->pCaptureThread == 0 )
				{
					it->second->pCaptureThread = new CaptureThread(it->second->pFuncInterface, pDev, pParentWindow);
					it->second->pCaptureThread->Create();
					it->second->pCaptureThread->Run();
					EventWaitResults waitResults = it->second->pEventSubSubsystem->waitFor( 0, detAll );
					if( waitResults.errorCode() != DMR_FEATURE_NOT_AVAILABLE )
					{
						it->second->pEventThread = new EventThread(pDev, it->second->pEventSubSubsystem, pParentWindow, m_boShowEvents);
						it->second->pEventThread->Create();
						it->second->pEventThread->Run();
					}
					it->second->pCaptureThread->SetActive();
				}
				if( it->second->pStatistics == 0 )
				{
					it->second->pStatistics = new Statistics( pDev );
				}
				it->second->boWasLive = false;
			}

			if( m_pFullTree )
			{
				m_pFullTree->Draw();
			}
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::PrepareTreeArray( void )
//-----------------------------------------------------------------------------
{
	m_vTreeProps.clear();
	switch( m_viewMode )
	{
	case vmDeveloper:
		m_vTreeProps.push_back( TreeProp(dltCameraDescriptions, "dmltCameraDescriptions" ) );
		m_vTreeProps.push_back( TreeProp(dltDeviceSpecificData, "dmltDeviceSpecificData") );
		m_vTreeProps.push_back( TreeProp(dltEventSubSystemSettings, "dmltEventSubSystemSettings") );
		m_vTreeProps.push_back( TreeProp(dltEventSubSystemResults, "dmltEventSubSystemResults") );
		m_vTreeProps.push_back( TreeProp(dltRequestCtrl, "dmltRequestCtrl") );
		m_vTreeProps.push_back( TreeProp(dltInfo, "dmltInfo") );
		m_vTreeProps.push_back( TreeProp(dltRequest, "dmltRequest") );
		m_vTreeProps.push_back( TreeProp(dltIOSubSystem, "dmltIOSubSystem") );
		m_vTreeProps.push_back( TreeProp(dltSetting, "dmltSetting") );
		m_vTreeProps.push_back( TreeProp(dltStatistics, "dmltStatistics") );
		m_vTreeProps.push_back( TreeProp(dltSystemSettings, "dmltSystemSettings") );
		m_vTreeProps.push_back( TreeProp(dltImageMemoryManager, "dmltImageMemoryManager") );
		break;
	default:
	case vmStandard:
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/Setting/Base", "Image Setting") );

#ifdef _SVSEM
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/Mixers", "Mixers") );
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/Acquisition", "Acquisition") );
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/InputModules", "InputModules") );
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/Scanner", "Scanner") );
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/CanBus", "CanBus") );
#endif //_SVSEM

		m_vTreeProps.push_back( TreeProp(dltCameraDescriptions, "Camera Descriptions" ) );
		m_vTreeProps.push_back( TreeProp("EventSubSystem", "Events") );
		m_vTreeProps.push_back( TreeProp(dltIOSubSystem, "Digital I/O") );
		m_vTreeProps.push_back( TreeProp(dltImageMemoryManager, "Image Memory Manager") );
		m_vTreeProps.push_back( TreeProp(dltInfo, "Info") );
		m_vTreeProps.push_back( TreeProp(dltRequest, "Requests") );
		m_vTreeProps.push_back( TreeProp(dltStatistics, "Statistics") );
		m_vTreeProps.push_back( TreeProp(dltSystemSettings, "System Settings") );
		m_vTreeProps.push_back( TreeProp(dltDeviceSpecificData, "Device Specific Data") );
		m_vTreeProps.push_back( TreeProp("Plugins", "Plugins") );

#ifdef _SVSEM
		m_vTreeProps.push_back( TreeProp("ImagingSubsystem/Example", "Example") );
#endif //_SVSEM
	
		break;
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::SetActiveDevice( const wxString& devName )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	string serialANSI(devName.mb_str());
	Device* pDev = m_devMgr.getDeviceBySerial( serialANSI );

	if( pDev )
	{
		UpdateTreeMap();
		DevToTreeMap::iterator itNewDev = m_devToTreeMap.find( pDev );

		if( itNewDev != m_devToTreeMap.end() )
		{
			if( m_pActiveDevice && ( m_pActiveDevice != pDev ) )
			{
				DevToTreeMap::iterator itCurDev = m_devToTreeMap.find( m_pActiveDevice );
				if( itCurDev != m_devToTreeMap.end() )
				{
					if( !m_pFullTree )
					{
						m_pPropertyGrid->Freeze();
						delete itCurDev->second->pDeviceTree;
						itCurDev->second->pDeviceTree = 0;
						DeleteDriverTrees( itCurDev );
						m_pPropertyGrid->Thaw();
					}
					CaptureThread* pThread = itCurDev->second->pCaptureThread;
					if( pThread )
					{
						itCurDev->second->boWasLive = pThread->GetLiveMode();
						pThread->SetLiveMode(false);
					}
					else
					{
						itCurDev->second->boWasLive = false;
					}
				}
			}

			if( m_pActiveDevice != pDev )
			{
				m_pActiveDevice = pDev;
				m_pActiveDevData = itNewDev->second;

				if( m_pFullTree )
				{
					m_fullTreeChangedCounter = m_pFullTree->Draw();
				}
				else
				{
					if( !itNewDev->second->pDeviceTree )
					{
						itNewDev->second->pDeviceTree = new PropTree( pDev->hDev(), "Device", m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
					}
					m_actDevListChangedCounter = itNewDev->second->pDeviceTree->Draw();
					if( pDev->isOpen() )
					{
						CreateDriverTrees( itNewDev );
					}
				}

				PropTreeVector::size_type vSize = itNewDev->second->vDriverTrees.size();
				for( PropTreeVector::size_type i=0; i<vSize; i++ )
				{
					if( itNewDev->second->vDriverTrees[i] )
					{
						m_vTreeProps[i].ChangedCounter = itNewDev->second->vDriverTrees[i]->Draw();
					}
				}

				CaptureThread* pThread = itNewDev->second->pCaptureThread;
				if( pThread )
				{
					pThread->SetActive();
					if( itNewDev->second->boWasLive )
					{
						itNewDev->second->boWasLive = pThread->GetLiveMode();
						pThread->SetLiveMode( true );
					}
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::SetViewMode( TViewMode mode, bool boUseHexIndices )
//-----------------------------------------------------------------------------
{
	if( ( mode != m_viewMode ) || ( boUseHexIndices != m_boUseHexIndices ) )
	{
		m_viewMode = mode;
		m_boUseHexIndices = boUseHexIndices;
		if( !m_pFullTree )
		{
			PrepareTreeArray();
			DevToTreeMap::iterator itCurDev = m_devToTreeMap.find( m_pActiveDevice );
			if( itCurDev != m_devToTreeMap.end() )
			{
				DeleteDriverTrees( itCurDev );
				CreateDriverTrees( itCurDev );
			}
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::UpdateTreeMap()
//-----------------------------------------------------------------------------
{
	if( m_devMgr.deviceCount() != m_devToTreeMap.size() )
	{
		for( unsigned int i=0; i<m_devMgr.deviceCount(); i++ )
		{
			DevToTreeMap::iterator it = m_devToTreeMap.find( m_devMgr[i] );
			if( it == m_devToTreeMap.end() )
			{
				m_devToTreeMap.insert( make_pair( m_devMgr[i], new DeviceData( m_pFullTree ? 0 : new PropTree( m_devMgr[i]->hDev(), "Device", m_pPropertyGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) ) ) ) );
			}
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::ValidateTrees( void )
//-----------------------------------------------------------------------------
{
	bool boUpdate = false;

	if( m_pFullTree )
	{
		if( m_fullTreeChangedCounter != ComponentList(ROOT_LIST).changedCounter() )
		{
			m_fullTreeChangedCounter = m_pFullTree->Draw();
			boUpdate = true;
		}
	}
	else if( m_pActiveDevData )
	{
		if(	m_actDevListChangedCounter != m_pActiveDevData->pDeviceTree->GetChangedCounter()   )
		{
			m_actDevListChangedCounter = m_pActiveDevData->pDeviceTree->Draw();
			boUpdate = true;
		}

		PropTreeVector::size_type vSize = m_pActiveDevData->vDriverTrees.size();
		for( PropTreeVector::size_type i=0; i<vSize; i++ )
		{
			if( m_pActiveDevData->vDriverTrees[i] &&
				( m_pActiveDevData->vDriverTrees[i]->GetChangedCounter() != m_vTreeProps[i].ChangedCounter )  )
			{
				m_vTreeProps[i].ChangedCounter = m_pActiveDevData->vDriverTrees[i]->Draw();
				boUpdate = true;
			}
		}
	}

	if( boUpdate )
	{
		m_pPropertyGrid->Refresh();
	}
}

