//-----------------------------------------------------------------------------
#include <apps/Common/wxAbstraction.h>
#include "CaptureThread.h"
#include "DataConversion.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_componentVisibility(cvBeginner), m_fullTreeChangedCounter(0), m_pFullTree(0), m_pActiveDevData(0), m_pActiveDevice(0),
	m_pPropertyGrid(pGrid), m_viewMode(vmStandard), m_boUseHexIndices(false), m_boUseDisplayNames(false), m_boUseSelectorGrouping(false)
//-----------------------------------------------------------------------------
{
	if( boDisplayFullTree )
	{
		m_pFullTree = new PropTree( ROOT_LIST, "Full Tree", pGrid, static_cast<EDisplayFlags>(GetDisplayFlags()) );
		m_pFullTree->Draw( false );
	}
}

//-----------------------------------------------------------------------------
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->pInfo;
		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;
			delete it->second->pInfo;
			it->second->pInfo = 0;
		}
		pDev->close();
	}

	if( m_pFullTree )
	{
		ReCreateFullTree();
	}
}

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

	if( ( m_viewMode == vmStandard ) && it->second->pInfo->recommendedListsForUIs.isValid() )
	{
		m_vTreeProps.clear();
		const unsigned int cnt = it->second->pInfo->recommendedListsForUIs.valCount();
		for( unsigned int i=0; i<cnt; i++ )
		{
			m_vTreeProps.push_back( TreeProp(it->second->pInfo->recommendedListsForUIs.read( i ), "") );
		}
#ifdef BUILD_WITH_PLUGIN_SUPPORT
		m_vTreeProps.push_back( TreeProp("Plugins", "Plugins") );
#endif // #ifdef BUILD_WITH_PLUGIN_SUPPORT
	}
	else
	{
		PrepareTreeArray();
	}

	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 )
			{
				Component comp(cl.findComponent( m_vTreeProps[i].ListName.c_str(), smIgnoreMethods | smIgnoreProperties ));
				string displayName(comp.displayName());
				if( displayName.empty() )
				{
					displayName = m_vTreeProps[i].DisplayName.empty() ? comp.name() : m_vTreeProps[i].DisplayName;
				}
				pTree = new PropTree( comp.hObj(), 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( false );
		}
		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);
	}
	if( m_boUseDisplayNames )
	{
		flags = EDisplayFlags(flags | dfDisplayNames);
	}
	if( m_boUseSelectorGrouping )
	{
		flags = EDisplayFlags(flags | dfSelectorGrouping);
	}
	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( !it->second->pEventSubSubsystem )
				{
					it->second->pEventSubSubsystem = new EventSubSystem(pDev);
				}
				if( !it->second->pFuncInterface )
				{
					it->second->pFuncInterface = new FunctionInterface(pDev);
				}
				if( !it->second->pCaptureThread )
				{
					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 )
				{
					it->second->pStatistics = new Statistics(pDev);
				}
				if( !it->second->pInfo )
				{
					it->second->pInfo = new Info(pDev);
				}
				if( !m_pFullTree &&
					( m_vTreeProps.empty() || ( it->second->vDriverTrees.size() != m_vTreeProps.size() ) ) )
				{
					CreateDriverTrees( it );
				}
				it->second->boWasLive = false;
			}

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

//-----------------------------------------------------------------------------
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;
	case vmStandard:
	default:
		m_vTreeProps.push_back( TreeProp(dltRequestCtrl, "Request Controls") );
		m_vTreeProps.push_back( TreeProp(dltSetting, "Image Settings") );
		PrepareTreeArrayCommon();
		break;
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::PrepareTreeArrayCommon( void )
//-----------------------------------------------------------------------------
{
	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") );
#ifdef BUILD_WITH_PLUGIN_SUPPORT
	m_vTreeProps.push_back( TreeProp("Plugins", "Plugins") );
#endif // #ifdef BUILD_WITH_PLUGIN_SUPPORT
}

//-----------------------------------------------------------------------------
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( false );
				}
				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( false );
					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( false );
					}
				}

				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, bool boUseDisplayNames, bool boUseSelectorGrouping )
//-----------------------------------------------------------------------------
{
	if( ( mode != m_viewMode ) || ( boUseHexIndices != m_boUseHexIndices ) ||
		( boUseDisplayNames != m_boUseDisplayNames ) || ( boUseSelectorGrouping != m_boUseSelectorGrouping ) )
	{
		m_viewMode = mode;
		m_boUseHexIndices = boUseHexIndices;
		m_boUseDisplayNames = boUseDisplayNames;
		m_boUseSelectorGrouping = boUseSelectorGrouping;
		if( m_pFullTree )
		{
			ReCreateFullTree();
		}
		else
		{
			ReCreateDriverTrees();
		}
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::ReCreateDriverTrees( void )
//-----------------------------------------------------------------------------
{
	DevToTreeMap::iterator itCurDev = m_devToTreeMap.find( m_pActiveDevice );
	if( itCurDev != m_devToTreeMap.end() )
	{
		DeleteDriverTrees( itCurDev );
		CreateDriverTrees( itCurDev );
	}
}

//-----------------------------------------------------------------------------
void DevicePropertyHandler::ReCreateFullTree( void )
//-----------------------------------------------------------------------------
{
	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( false );
}

//-----------------------------------------------------------------------------
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;
	bool boForceRedraw = false;

	if( m_componentVisibility != GlobalDataStorage::Instance()->GetComponentVisibility() )
	{
		boForceRedraw = true;
		m_componentVisibility = GlobalDataStorage::Instance()->GetComponentVisibility();
	}

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

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

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

