//-- private methods -----
static void drawController(PSMController *controllerView, const glm::mat4 &transform)
{
    switch(controllerView->ControllerType)
    {
    case PSMController_Move:
        drawPSMoveModel(transform, glm::vec3(1.f, 1.f, 1.f));
        break;
    case PSMController_DualShock4:
        drawPSDualShock4Model(transform, glm::vec3(1.f, 1.f, 1.f));
        break;
    }
}
//-- private methods -----
static void drawController(ClientControllerView *controllerView, const glm::mat4 &transform, const glm::vec3 &bulb_color)
{
    switch(controllerView->GetControllerViewType())
    {
    case ClientControllerView::PSMove:
        drawPSMoveModel(transform, bulb_color);
        break;
    case ClientControllerView::PSNavi:
        drawPSNaviModel(transform);
        break;
    case ClientControllerView::PSDualShock4:
        drawPSDualShock4Model(transform, bulb_color);
        break;
    }
}
//-- private methods -----
static void drawController(
	const ClientControllerView *controllerView, 
	const glm::mat4 &transform, 
	const PSMoveTrackingColorType trackingColorType)
{
	glm::vec3 bulb_color = glm::vec3(1.f, 1.f, 1.f);

	switch (trackingColorType)
	{
	case PSMoveTrackingColorType::Magenta:
		bulb_color = glm::vec3(1.f, 0.f, 1.f);
		break;
	case PSMoveTrackingColorType::Cyan:
		bulb_color = glm::vec3(0.f, 1.f, 1.f);
		break;
	case PSMoveTrackingColorType::Yellow:
		bulb_color = glm::vec3(1.f, 1.f, 0.f);
		break;
	case PSMoveTrackingColorType::Red:
		bulb_color = glm::vec3(1.f, 0.f, 0.f);
		break;
	case PSMoveTrackingColorType::Green:
		bulb_color = glm::vec3(0.f, 1.f, 0.f);
		break;
	case PSMoveTrackingColorType::Blue:
		bulb_color = glm::vec3(0.f, 0.f, 1.f);
		break;
	default:
		break;
	}

    switch(controllerView->GetControllerViewType())
    {
    case ClientControllerView::PSMove:
        drawPSMoveModel(transform, bulb_color);
        break;
    case ClientControllerView::PSDualShock4:
        drawPSDualShock4Model(transform, bulb_color);
        break;
    }
}
void AppStage_MagnetometerCalibration::render()
{
    const float modelScale = 18.f;
    glm::mat4 scaleAndRotateModelX90= 
        glm::rotate(
            glm::scale(glm::mat4(1.f), glm::vec3(modelScale, modelScale, modelScale)),
            90.f, glm::vec3(1.f, 0.f, 0.f));  
    
    PSMoveIntVector3 rawSampleExtents = (m_maxSampleExtent - m_minSampleExtent).unsafe_divide(2);

    glm::vec3 boxMin = psmove_float_vector3_to_glm_vec3(m_minSampleExtent.castToFloatVector3());
    glm::vec3 boxMax = psmove_float_vector3_to_glm_vec3(m_maxSampleExtent.castToFloatVector3());
    glm::vec3 boxCenter = (boxMax + boxMin) * 0.5f;
    glm::vec3 boxExtents = (boxMax - boxMin) * 0.5f;

    glm::mat4 recenterMatrix = 
        glm::translate(glm::mat4(1.f), -eigen_vector3f_to_glm_vec3(m_sampleFitEllipsoid.center));

    switch (m_menuState)
    {
    case eCalibrationMenuState::waitingForStreamStartResponse:
        {
        } break;
    case eCalibrationMenuState::failedStreamStart:
    case eCalibrationMenuState::failedBadCalibration:
        {
        } break;
    case eCalibrationMenuState::measureBExtents:
        {

            float r= clampf01(static_cast<float>(m_led_color_r) / 255.f);
            float g= clampf01(static_cast<float>(m_led_color_g) / 255.f);
            float basis= clampf01(static_cast<float>(m_led_color_b) / 255.f);

            // Draw the psmove model in the middle
            drawPSMoveModel(scaleAndRotateModelX90, glm::vec3(r, g, basis));

            // Draw the sample point cloud around the origin
            drawPointCloud(
                recenterMatrix,
                glm::vec3(1.f, 1.f, 1.f),
                reinterpret_cast<float *>(&m_alignedSamples->magnetometerEigenSamples[0]),
                m_sampleCount);

            // Draw the sample bounding box
            // Label the min and max corners with the min and max magnetometer readings
            drawTransformedBox(recenterMatrix, boxMin, boxMax, glm::vec3(1.f, 1.f, 1.f));
            drawTextAtWorldPosition(recenterMatrix, boxMin, "%d,%d,%d",
                                    m_minSampleExtent.i, m_minSampleExtent.j, m_minSampleExtent.k);
            drawTextAtWorldPosition(recenterMatrix, boxMax, "%d,%d,%d",
                                    m_maxSampleExtent.i, m_maxSampleExtent.j, m_maxSampleExtent.k);

            // Draw and label the extent axes
            drawTransformedAxes(glm::mat4(1.f), boxExtents.x, boxExtents.y, boxExtents.z);
            drawTextAtWorldPosition(glm::mat4(1.f), glm::vec3(boxExtents.x, 0.f, 0.f), "%d", rawSampleExtents.i);
            drawTextAtWorldPosition(glm::mat4(1.f), glm::vec3(0.f, boxExtents.y, 0.f), "%d", rawSampleExtents.j);
            drawTextAtWorldPosition(glm::mat4(1.f), glm::vec3(0.f, 0.f, boxExtents.z), "%d", rawSampleExtents.k);

            // Draw the best fit ellipsoid
            {
                glm::mat3 basis = eigen_matrix3f_to_glm_mat3(m_sampleFitEllipsoid.basis);
                glm::vec3 center = eigen_vector3f_to_glm_vec3(m_sampleFitEllipsoid.center);
                glm::vec3 extents = eigen_vector3f_to_glm_vec3(m_sampleFitEllipsoid.extents);

                drawEllipsoid(
                    recenterMatrix,
                    glm::vec3(0.f, 0.4f, 1.f),
                    basis, center, extents);
                drawTextAtWorldPosition(
                    recenterMatrix,
                    center - basis[0]*extents.x,
                    "E:%.1f", m_sampleFitEllipsoid.error);
            }

            // Draw the current magnetometer direction
            {
                glm::vec3 m_start= boxCenter;
                glm::vec3 m_end= psmove_float_vector3_to_glm_vec3(m_lastMagnetometer.castToFloatVector3());

                drawArrow(recenterMatrix, m_start, m_end, 0.1f, glm::vec3(1.f, 0.f, 0.f));
                drawTextAtWorldPosition(recenterMatrix, m_end, "M");
            }
        } break;
    case eCalibrationMenuState::waitForGravityAlignment:
        {
            drawPSMoveModel(scaleAndRotateModelX90, glm::vec3(1.f, 1.f, 1.f));

            // Draw the current direction of gravity
            {
                const float renderScale = 200.f;
                glm::mat4 renderScaleMatrix = 
                    glm::scale(glm::mat4(1.f), glm::vec3(renderScale, renderScale, renderScale));
                glm::vec3 g= psmove_float_vector3_to_glm_vec3(m_lastAccelerometer);

                drawArrow(
                    renderScaleMatrix,
                    glm::vec3(), g, 
                    0.1f, 
                    glm::vec3(0.f, 1.f, 0.f));
                drawTextAtWorldPosition(renderScaleMatrix, g, "G");
            }
        } break;
    case eCalibrationMenuState::measureBDirection:
        {
            drawPSMoveModel(scaleAndRotateModelX90, glm::vec3(1.f, 1.f, 1.f));

            // Draw the current magnetometer direction
            {
                glm::vec3 m_start = boxCenter;
                glm::vec3 m_end = psmove_float_vector3_to_glm_vec3(m_lastMagnetometer.castToFloatVector3());

                drawArrow(recenterMatrix, m_start, m_end, 0.1f, glm::vec3(1.f, 0.f, 0.f));
                drawTextAtWorldPosition(recenterMatrix, m_end, "M");
            }

        } break;
    case eCalibrationMenuState::waitForSetCalibrationResponse:
        {
        } break;
    case eCalibrationMenuState::failedSetCalibration:
        {
        } break;
    case eCalibrationMenuState::complete:
        {
            // Get the orientation of the controller in world space (OpenGL Coordinate System)            
            glm::quat q= psmove_quaternion_to_glm_quat(m_controllerView->GetPSMoveView().GetOrientation());
            glm::mat4 worldSpaceOrientation= glm::mat4_cast(q);
            glm::mat4 worldTransform = glm::scale(worldSpaceOrientation, glm::vec3(modelScale, modelScale, modelScale));

            drawPSMoveModel(worldTransform, glm::vec3(1.f, 1.f, 1.f));
            drawTransformedAxes(glm::mat4(1.f), 200.f);
        } break;
    case eCalibrationMenuState::pendingExit:
        {
        } break;
    default:
        assert(0 && "unreachable");
    }
}
void AppStage_ControllerSettings::render()
{
    glm::mat4 scale2RotateX90= 
        glm::rotate(
            glm::scale(glm::mat4(1.f), glm::vec3(2.f, 2.f, 2.f)), 
            90.f, glm::vec3(1.f, 0.f, 0.f));    

    switch (m_menuState)
    {
    case eControllerMenuState::idle:
        {
            if (m_selectedControllerIndex >= 0)
            {
                const ControllerInfo &controllerInfo= m_bluetoothControllerInfos[m_selectedControllerIndex];

                switch(controllerInfo.ControllerType)
                {
                    case PSMoveProtocol::PSMOVE:
                        {
                            const ControllerInfo &controllerInfo = m_bluetoothControllerInfos[m_selectedControllerIndex];

                            // Display the tracking color being used for the controller
                            glm::vec3 bulb_color = glm::vec3(1.f, 1.f, 1.f);

                            switch (controllerInfo.TrackingColorType)
                            {
                            case PSMoveTrackingColorType::Magenta:
                                bulb_color = glm::vec3(1.f, 0.f, 1.f);
                                break;
                            case PSMoveTrackingColorType::Cyan:
                                bulb_color = glm::vec3(0.f, 1.f, 1.f);
                                break;
                            case PSMoveTrackingColorType::Yellow:
                                bulb_color = glm::vec3(1.f, 1.f, 0.f);
                                break;
                            case PSMoveTrackingColorType::Red:
                                bulb_color = glm::vec3(1.f, 0.f, 0.f);
                                break;
                            case PSMoveTrackingColorType::Green:
                                bulb_color = glm::vec3(0.f, 1.f, 0.f);
                                break;
                            case PSMoveTrackingColorType::Blue:
                                bulb_color = glm::vec3(0.f, 0.f, 1.f);
                                break;
                            default:
                                break;
                            }

                            drawPSMoveModel(scale2RotateX90, bulb_color);
                        } break;
                    case PSMoveProtocol::PSNAVI:
                        {
                            drawPSNaviModel(scale2RotateX90);
                        } break;
                    default:
                        assert(0 && "Unreachable");
                }        
            }
        } break;

    case eControllerMenuState::pendingControllerListRequest:
    case eControllerMenuState::failedControllerListRequest:
        {
        } break;

    default:
        assert(0 && "unreachable");
    }
}
void AppStage_ComputeTrackerPoses::render()
{
    switch (m_menuState)
    {
    case eMenuState::inactive:
        break;
    case eMenuState::pendingControllerListRequest:
    case eMenuState::pendingControllerStartRequest:
    case eMenuState::pendingTrackerListRequest:
    case eMenuState::pendingTrackerStartRequest:
        break;
    case eMenuState::failedControllerListRequest:
    case eMenuState::failedControllerStartRequest:
    case eMenuState::failedTrackerListRequest:
    case eMenuState::failedTrackerStartRequest:
        break;
    case eMenuState::verifyHMD:
        {
            if (m_hmdView != nullptr)
            {
                PSMovePose pose = m_hmdView->getDisplayHmdPose();
                glm::quat orientation(pose.Orientation.w, pose.Orientation.x, pose.Orientation.y, pose.Orientation.z);
                glm::vec3 position(pose.Position.x, pose.Position.y, pose.Position.z);

                glm::mat4 rot = glm::mat4_cast(orientation);
                glm::mat4 trans = glm::translate(glm::mat4(1.0f), position);
                glm::mat4 transform = trans * rot;

                drawDK2Model(transform);
                drawTransformedAxes(transform, 10.f);
            }

            {
                PSMoveVolume volume;

                if (m_app->getOpenVRContext()->getHMDTrackingVolume(volume))
                {
                    drawTransformedVolume(glm::mat4(1.f), &volume, glm::vec3(0.f, 1.f, 1.f));
                }
            }
        } break;
    case eMenuState::verifyTrackers:
        {
            render_tracker_video();
        } break;
    case eMenuState::selectCalibrationType:
        break;
    case eMenuState::calibrateWithHMD:
        m_pCalibrateWithHMD->render();
        break;
    case eMenuState::calibrateWithMat:
        m_pCalibrateWithMat->render();
        break;
    case eMenuState::testTracking:
        {
            // Draw the origin axes
            drawTransformedAxes(glm::mat4(1.0f), 100.f);

            // Draw the HMD and tracking volume
            if (m_hmdView != nullptr)
            {
                // Compute a transform that goes from HMD tracking space to PSMove tracking space
                PSMovePose hmd_pose_at_origin = m_app->getOpenVRContext()->getHMDPoseAtPSMoveTrackingSpaceOrigin();
                glm::mat4 tracking_space_transform = psmove_pose_to_glm_mat4(hmd_pose_at_origin);
                glm::mat4 tracking_space_inv_transform = glm::inverse(tracking_space_transform);

                // Put the HMD transform in PSMove tracking space
                PSMovePose hmd_pose = m_hmdView->getDisplayHmdPose();
                glm::mat4 hmd_transform = tracking_space_inv_transform * psmove_pose_to_glm_mat4(hmd_pose);

                drawDK2Model(hmd_transform);
                drawTransformedAxes(hmd_transform, 10.f);

                PSMoveVolume volume;
                if (m_app->getOpenVRContext()->getHMDTrackingVolume(volume))
                {
                    drawTransformedVolume(tracking_space_inv_transform, &volume, glm::vec3(0.f, 1.f, 1.f));
                }
            }

            // Draw the frustum for each tracking camera
            for (t_tracker_state_map_iterator iter = m_trackerViews.begin(); iter != m_trackerViews.end(); ++iter)
            {
                const ClientTrackerView *trackerView = iter->second.trackerView;

                {
                    PSMoveFrustum frustum = trackerView->getTrackerFrustum();

                    drawFrustum(&frustum, k_psmove_frustum_color);
                }

                {
                    PSMovePose pose = trackerView->getTrackerPose();
                    glm::mat4 cameraTransform = psmove_pose_to_glm_mat4(pose);

                    drawTransformedAxes(cameraTransform, 20.f);
                }
            }

            // Draw the psmove model
            {
                PSMovePose pose = m_controllerView->GetPSMoveView().GetPose();
                glm::mat4 worldTransform = psmove_pose_to_glm_mat4(pose);

                drawPSMoveModel(worldTransform, glm::vec3(1.f, 1.f, 1.f));
                drawTransformedAxes(worldTransform, 10.f);
            }

        } break;
    case eMenuState::calibrateStepFailed:
        break;
    default:
        assert(0 && "unreachable");
    }
}