// The hydra_monitor is a companion program which can display overlay prompts for us
// and tell us the pose of the HMD at the moment we want to calibrate.
void CServerDriver_Hydra::LaunchHydraMonitor( const char * pchDriverInstallDir )
{
	if ( m_bLaunchedHydraMonitor )
		return;

	m_bLaunchedHydraMonitor = true;

	std::ostringstream ss;

	ss << pchDriverInstallDir << "\\bin\\";
#if defined( _WIN64 )
	ss << "win64";
#elif defined( _WIN32 )
	ss << "win32";
#else
#warning Do not know how to launch hydra_monitor
#endif
	DriverLog( "hydra_monitor path: %s\n", ss.str().c_str() );

#if defined( _WIN32 )
	STARTUPINFOA sInfoProcess = { 0 };
	sInfoProcess.cb = sizeof( STARTUPINFOW );
	PROCESS_INFORMATION pInfoStartedProcess;
	BOOL okay = CreateProcessA( (ss.str() + "\\hydra_monitor.exe").c_str(), NULL, NULL, NULL, FALSE, 0, NULL, ss.str().c_str(), &sInfoProcess, &pInfoStartedProcess );
	DriverLog( "start hydra_monitor okay: %d %08x\n", okay, GetLastError() );
#else
#warning Do not know how to launch hydra_monitor
#endif
}
// The leap_monitor is a companion program which will tell us the pose of the HMD.
void CServerDriver_Leap::LaunchLeapMonitor( const char * pchDriverInstallDir )
{
    if ( m_bLaunchedLeapMonitor )
        return;

    DriverLog("CServerDriver_Leap::LaunchLeapMonitor()\n");

    m_bLaunchedLeapMonitor = true;

    std::ostringstream ss;

    ss << pchDriverInstallDir << "\\bin\\";
#if defined( _WIN64 )
    ss << "win64";
#elif defined( _WIN32 )
    ss << "win32";
#else
#error Do not know how to launch leap_monitor
#endif
    DriverLog( "leap_monitor path: %s\n", ss.str().c_str() );

#if defined( _WIN32 )
    STARTUPINFOA sInfoProcess = { 0 };
    sInfoProcess.cb = sizeof(STARTUPINFOW);
//    sInfoProcess.dwFlags = STARTF_USESHOWWINDOW;
//    sInfoProcess.wShowWindow = SW_SHOWDEFAULT;
    BOOL okay = CreateProcessA( (ss.str() + "\\leap_monitor.exe").c_str(), NULL, NULL, NULL, FALSE, 0, NULL, ss.str().c_str(), &sInfoProcess, &m_pInfoStartedProcess );
    DriverLog( "start leap_monitor okay: %d %08x\n", okay, GetLastError() );
#else
#error Do not know how to launch leap_monitor
#endif
}
vr::EVRInitError CClientDriver_Leap::Init( vr::IDriverLog * pDriverLog, vr::IClientDriverHost * pDriverHost, const char * pchUserDriverConfigDir, const char * pchDriverInstallDir )
{
    InitDriverLog( pDriverLog );
    DriverLog("CClientDriver_Leap::Init()\n");
    m_pDriverHost = pDriverHost;
    return vr::VRInitError_None;
}
CLeapHmdLatest::CLeapHmdLatest( vr::IServerDriverHost * pDriverHost, int base, int n )
    : m_pDriverHost( pDriverHost )
    , m_nBase( base )
    , m_nId( n )
    , m_bCalibrated( true )
    , m_pAlignmentPartner( NULL )
    , m_unSteamVRTrackedDeviceId( vr::k_unTrackedDeviceIndexInvalid )
{
    DriverLog("CLeapHmdLatest::CLeapHmdLatest(base=%d, n=%d)\n", base, n);
    
    memset(m_hmdPos, 0, sizeof(m_hmdPos));

    char buf[256];
    GenerateSerialNumber( buf, sizeof( buf ), base, n );
    m_strSerialNumber = buf;

    memset( &m_ControllerState, 0, sizeof( m_ControllerState ) );
    memset( &m_Pose, 0, sizeof( m_Pose ) );
    m_Pose.result = vr::TrackingResult_Uninitialized;

    m_firmware_revision = 0x0001;
    m_hardware_revision = 0x0001;

    // Load config from steamvr.vrsettings
    vr::IVRSettings *settings_;
    settings_ = m_pDriverHost->GetSettings(vr::IVRSettings_Version);

    // Load rendermodel
    char tmp_[256];
    settings_->GetString("leap", (m_nId ==  LEFT_CONTROLLER) ? "renderModel_lefthand" : (m_nId == RIGHT_CONTROLLER) ? "renderModel_righthand" : "renderModel", tmp_, sizeof(tmp_), "vr_controller_vive_1_5");
    m_strRenderModel = tmp_;

    // set the 
    m_gripAngleOffset = settings_->GetFloat("leap", (m_nId == LEFT_CONTROLLER) ? "gripAngleOffset_lefthand" : (m_nId == RIGHT_CONTROLLER) ? "gripAngleOffset_righthand" : "gripAngleOffset", 0.0);
}
void CServerDriver_Leap::Cleanup()
{
    DriverLog("CServerDriver_Leap::Cleanup()\n");

    // send a termination message to the leap monitor companion application
    if (m_bLaunchedLeapMonitor)
    {
        // Ask leap_monitor to shut down.
        PostThreadMessage(m_pInfoStartedProcess.dwThreadId, WM_QUIT, 0, 0);
        m_bLaunchedLeapMonitor = false;
    }

    // clean up our Leap::Controller object
    if (m_Controller)
    {
        m_Controller->removeListener(*this);
        delete m_Controller;
        m_Controller = NULL;
    }

    // clean up any controller objects we've created
    for (auto it = m_vecControllers.begin(); it != m_vecControllers.end(); ++it)
        delete (*it);
    m_vecControllers.clear();
}
void CServerDriver_Hydra::ScanForNewControllers( bool bNotifyServer )
{
	for ( int base = 0; base < sixenseGetMaxBases(); ++base )
	{
		if ( sixenseIsBaseConnected( base ) )
		{
			sixenseSetActiveBase( base );
			for ( int i = 0; i < sixenseGetMaxControllers(); ++i )
			{
				if ( sixenseIsControllerEnabled( i ) )
				{
					char buf[256];
					GenerateSerialNumber( buf, sizeof( buf ), base, i );
					scope_lock lock( m_Mutex );
					if ( !FindTrackedDeviceDriver( buf, vr::ITrackedDeviceServerDriver_Version ) )
					{
						DriverLog( "added new device %s\n", buf );
						m_vecControllers.push_back( new CHydraHmdLatest( m_pDriverHost, base, i ) );
						if ( bNotifyServer && m_pDriverHost )
						{
							m_pDriverHost->TrackedDeviceAdded( m_vecControllers.back()->GetSerialNumber() );
						}
					}
				}
			}
		}
	}
}
/**
 * Called when a Leap Motion controller is plugged in, unplugged, or the device changes state.
 *
 * State changes include entering or leaving robust mode and low resource mode.
 * Note that there is no direct way to query whether the device is in these modes,
 * although you can use Controller::isLightingBad() to check if there are environmental
 * IR lighting problems.
 *
 * \include Listener_onDeviceChange.txt
 *
 * @param controller The Controller object invoking this callback function.
 * @since 1.2
 */
void CServerDriver_Leap::onDeviceChange(const Controller&controller)
{
    DriverLog("CServerDriver_Leap::onDeviceChange()\n");

    if (controller.isConnected())
    {
        bool backgroundModeAllowed = controller.config().getInt32("background_app_mode") == 2;
        if (!backgroundModeAllowed) {
            // TODO: Show dialog to request permission to allow background mode apps
            bool userPermission = true;
            if (userPermission) {
                controller.config().setInt32("background_app_mode", 2);
                controller.config().save();
            }
        }

        controller.setPolicy(Leap::Controller::POLICY_OPTIMIZE_HMD);
        controller.setPolicy(Leap::Controller::POLICY_BACKGROUND_FRAMES);

        // make sure we always get background frames even when we lose the focus to another
        // Leap-enabled application
        controller.setPolicy((Leap::Controller::PolicyFlag)(15));

        // allow other background applications to receive frames even when SteamVR has the focus.
        controller.setPolicy((Leap::Controller::PolicyFlag)(23));

        ScanForNewControllers(true);
    }
    else
    {
        for (auto it = m_vecControllers.begin(); it != m_vecControllers.end(); ++it)
            delete (*it);
        m_vecControllers.clear();
    }
}
vr::EVRInitError CHydraHmdLatest::Activate( uint32_t unObjectId )
{
	DriverLog( "CHydraHmdLatest::Activate: %s is object id %d\n", GetSerialNumber(), unObjectId );
	m_unSteamVRTrackedDeviceId = unObjectId;

	g_ServerTrackedDeviceProvider.LaunchHydraMonitor();

	return vr::VRInitError_None;
}
vr::EVRInitError CServerDriver_Leap::Init( vr::IDriverLog * pDriverLog, vr::IServerDriverHost * pDriverHost, const char * pchUserDriverConfigDir, const char * pchDriverInstallDir )
{
    InitDriverLog( pDriverLog );
    DriverLog("CServerDriver_Leap::Init()\n");

    m_pDriverHost = pDriverHost;
    m_strDriverInstallDir = pchDriverInstallDir;

    m_Controller = new Controller;

    Controller &controller = *m_Controller;

    m_Controller->addListener(*this);

    return vr::VRInitError_None;
}
vr::ITrackedDeviceServerDriver * CServerDriver_Hydra::GetTrackedDeviceDriver( uint32_t unWhich, const char *pchInterfaceVersion )
{
	// don't return anything if that's not the interface version we have
	if ( 0 != stricmp( pchInterfaceVersion, vr::ITrackedDeviceServerDriver_Version ) )
	{
		DriverLog( "FindTrackedDeviceDriver for version %s, which we don't support.\n",
			pchInterfaceVersion );
		return NULL;
	}

	scope_lock lock( m_Mutex );

	if ( unWhich < m_vecControllers.size() )
		return m_vecControllers[unWhich];

	return nullptr;
}
void CServerDriver_Leap::ScanForNewControllers( bool bNotifyServer )
{
    while (m_vecControllers.size() < 2)
    {
        char buf[256];
        int base = 0;
        int i = m_vecControllers.size();
        GenerateSerialNumber( buf, sizeof( buf ), base, i );
        if ( !FindTrackedDeviceDriver( buf ) )
        {
            DriverLog( "added new device %s\n", buf );
            m_vecControllers.push_back( new CLeapHmdLatest( m_pDriverHost, base, i ) );
            if ( bNotifyServer && m_pDriverHost )
            {
                m_pDriverHost->TrackedDeviceAdded( m_vecControllers.back()->GetSerialNumber() );
            }
        }
    }
}
vr::ITrackedDeviceServerDriver * CServerDriver_Hydra::FindTrackedDeviceDriver( const char * pchId, const char *pchInterfaceVersion )
{
	// don't return anything if that's not the interface version we have
	if ( 0 != stricmp( pchInterfaceVersion, vr::ITrackedDeviceServerDriver_Version ) )
	{
		DriverLog( "FindTrackedDeviceDriver for version %s, which we don't support.\n",
			pchInterfaceVersion );
		return NULL;
	}

	scope_lock lock( m_Mutex );

	for ( auto it = m_vecControllers.begin(); it != m_vecControllers.end(); ++it )
	{
		if ( 0 == strcmp( ( *it )->GetSerialNumber(), pchId ) )
		{
			return *it;
		}
	}
	return nullptr;
}
/**
 * Called if the Leap Motion daemon/service disconnects from your application Controller.
 *
 * Normally, this callback is not invoked. It is only called if some external event
 * or problem shuts down the service or otherwise interrupts the connection.
 *
 * \include Listener_onServiceDisconnect.txt
 *
 * @param controller The Controller object invoking this callback function.
 * @since 1.2
 */
void CServerDriver_Leap::onServiceDisconnect(const Controller&controller)
{
    DriverLog("CServerDriver_Leap::onServiceDisconnect()\n");
}
/**
 * Called when a Leap Motion controller device is plugged into the client
 * computer, but fails to operate properly.
 *
 * Get the list containing all failed devices using Controller::failedDevices().
 * The members of this list provide the device pnpID and reason for failure.
 *
 * \include Listener_onDeviceFailure.txt
 *
 * @param controller The Controller object invoking this callback function.
 * @since 3.0
 */
void CServerDriver_Leap::onDeviceFailure(const Controller&controller)
{
    DriverLog("CServerDriver_Leap::onDeviceFailure()\n");
}
/**
 * Called when the service emits a log message to report an error, warning, or
 * status change.
 *
 * Log message text is provided as ASCII-encoded english.
 *
 * @param controller The Controller object invoking this callback function.
 * @param severity The severity of the error, if known.
 * @param timestamp The timestamp of the error in microseconds.
 * (Use Controller::now() - timestamp to compute the age of the message.)
 * @param msg The log message.
 * @since 3.0
 */
void CServerDriver_Leap::onLogMessage(const Controller&controller, MessageSeverity severity, int64_t timestamp, const char* msg)
{
    DriverLog("CServerDriver_Leap::onLogMessage(%d): %s\n", (int)severity, msg);
}
CLeapHmdLatest::~CLeapHmdLatest()
{
    DriverLog("CLeapHmdLatest::~CLeapHmdLatest(base=%d, n=%d)\n", m_nBase, m_nId);
}
void CLeapHmdLatest::PowerOff()
{
    DriverLog("CLeapHmdLatest::PowerOff()\n");
    // FIXME Implement
}
CServerDriver_Leap::~CServerDriver_Leap()
{
    DriverLog("CServerDriver_Leap::~CServerDriver_Leap()\n");
    Cleanup();
}
void CClientDriver_Leap::Cleanup()
{
    DriverLog("CClientDriver_Leap::Cleanup()\n");
}
void CServerDriver_Leap::EnterStandby()
{
    DriverLog("CServerDriver_Leap::EnterStandby()\n");
}
/**
 * Called when the Leap Motion service is paused or resumed or when a
 * controller policy is changed.
 *
 * The service can change states because the computer user changes settings
 * in the Leap Motion Control Panel application or because an application
 * connected to the service triggers a change. Any application can pause or
 * unpause the service, but only runtime policy changes you make apply to your
 * own application.
 *
 * \include Listener_onServiceChange.txt
 *
 * You can query the pause state of the controller with Controller::isPaused().
 * You can check the state of those policies you are interested in with
 * Controller::isPolicySet().
 *
 * @param controller The Controller object invoking this callback function.
 * @since 3.0
 */
void CServerDriver_Leap::onServiceChange(const Controller&controller)
{
    DriverLog("CServerDriver_Leap::onServiceChange()\n");
}
void CServerDriver_Leap::LeaveStandby()
{
    DriverLog("CServerDriver_Leap::LeaveStandby()\n");
}
void CHydraHmdLatest::Deactivate()
{
	DriverLog( "CHydraHmdLatest::Deactivate: %s was object id %d\n", GetSerialNumber(), m_unSteamVRTrackedDeviceId );
	m_unSteamVRTrackedDeviceId = vr::k_unTrackedDeviceIndexInvalid;
}
/**
 * Called when this Listener object is removed from the Controller
 * or the Controller instance is destroyed.
 *
 * \include Listener_onExit.txt
 *
 * @param controller The Controller object invoking this callback function.
 * @since 1.0
 */
void CServerDriver_Leap::onExit(const Controller&controller)
{
    DriverLog("CServerDriver_Leap::onExit()\n");
}