/*****************************************************************************
 * $LastChangedDate: 2011-04-09 21:58:06 -0400 (Sat, 09 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Keyboard-controlled manipulator (OSG specific).
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define VIEW_MANIPULATOR_KEYBOARD_CC 1
#include "base/module.hh"
using namespace base;
#include "math/module.hh"
#include "math/defs_trig.hh"
#include "math/funcs_trig.hh"
#include "math/matrix.hh"
#include "math/matrix_funcs.hh"
using namespace math;
#include "control/module.hh"
#include "control/defs_axis.hh"
using namespace control;
#include "view/module.hh"
#include "view/conf.hh"
#include "view/manipulator.hh"
#include "view/manipulator_keyboard.hh"

namespace view {

////////////////////////////////////////////////////////////////////////////////
//////////////////////////  KeyboardManipulator  ///////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
KeyboardManipulator::KeyboardManipulator( void )
:   mMatrix(),
    mMatrixDefault(),
    mMatrixSaved(),
    mYawRad(VIEW_CONF.mManipulatorYawRad),
    mPitchRad(VIEW_CONF.mManipulatorPitchRad),
    mStep(VIEW_CONF.mManipulatorStep)
{
    // Set the default orientation/position defined in text configuration.
    // Can be overridden from binary configuration if user presses SPACE key.
    VIEW_CONF.mManipulatorRotationList.Apply( mMatrix );
    mMatrix[Ox] = VIEW_CONF.mManipulatorPosition[XX];
    mMatrix[Oy] = VIEW_CONF.mManipulatorPosition[YY];
    mMatrix[Oz] = VIEW_CONF.mManipulatorPosition[ZZ];

    // Save matrix for going to "home".
    mMatrixDefault = mMatrix;
    mMatrixSaved   = mMatrix;
}

KeyboardManipulator::~KeyboardManipulator()
{
    // NOP
}

/*****************************************************************************
 * Restore matrix from a saved copy.
 *****************************************************************************/
void
KeyboardManipulator::RestoreMatrix( const Matrix& srcMatrix )
{
    mMatrix = srcMatrix;
}

/*****************************************************************************
 * Restore the viewpoint.
 *****************************************************************************/
void
KeyboardManipulator::home( double currentTime )
{
    RestoreMatrix( mMatrixSaved );
}

void
KeyboardManipulator::home( const osgGA::GUIEventAdapter& event, osgGA::GUIActionAdapter& action )
{
    RestoreMatrix( mMatrixSaved );
}

/*****************************************************************************
 * Handle keyboard events.
 *****************************************************************************/
bool
KeyboardManipulator::handle( const osgGA::GUIEventAdapter& event, osgGA::GUIActionAdapter& action )
{
    if ( event.getEventType() & osgGA::GUIEventAdapter::KEYDOWN )
    {
        const int key = event.getKey();
        const bool modifier = event.getModKeyMask() & (osgGA::GUIEventAdapter::MODKEY_SHIFT
                                                      |osgGA::GUIEventAdapter::MODKEY_CTRL
                                                      |osgGA::GUIEventAdapter::MODKEY_ALT
                                                      |osgGA::GUIEventAdapter::MODKEY_META
                                                      |osgGA::GUIEventAdapter::MODKEY_SUPER);  // Apple Cmd or Win key
        switch ( key )
        {
            case osgGA::GUIEventAdapter::KEY_Left:
            case osgGA::GUIEventAdapter::KEY_KP_Left:
            {
                MoveLeftRightOrYaw( 1, modifier );
                return true;
            }
            break;

            case osgGA::GUIEventAdapter::KEY_Right:
            case osgGA::GUIEventAdapter::KEY_KP_Right:
            {
                MoveLeftRightOrYaw( -1, modifier );
                return true;
            }
            break;

            case osgGA::GUIEventAdapter::KEY_Up:
            case osgGA::GUIEventAdapter::KEY_KP_Up:
            {
                MoveForwardBackwardOrPitch( -1, modifier );
                return true;
            }
            break;

            case osgGA::GUIEventAdapter::KEY_Down:
            case osgGA::GUIEventAdapter::KEY_KP_Down:
            {
                MoveForwardBackwardOrPitch( 1, modifier );
                return true;
            }
            break;

            case osgGA::GUIEventAdapter::KEY_Page_Up:
            case osgGA::GUIEventAdapter::KEY_KP_Page_Up:
            {
                MoveUpDown( 1 );
                return true;
            }
            break;

            case osgGA::GUIEventAdapter::KEY_Page_Down:
            case osgGA::GUIEventAdapter::KEY_KP_Page_Down:
            {
                MoveUpDown( -1 );
                return true;
            }
            break;

            // Save/restore viewpoint.
            case ' ':
            {
                if ( modifier )
                    mMatrixSaved = mMatrix;  // save
                else
                    mMatrix = mMatrixSaved;  // restore
                return true;
            }
            break;

            // Restore default viewpoint (differs from the saved viewpoint).
            case osgGA::GUIEventAdapter::KEY_BackSpace:
            case osgGA::GUIEventAdapter::KEY_Delete:
            case osgGA::GUIEventAdapter::KEY_KP_Delete:
            {
                RestoreMatrix( mMatrixDefault );
                return true;
            }
            break;
        }
    }

    return false;
}

/*****************************************************************************
 * Move left/right or yaw.
 *****************************************************************************/
void
KeyboardManipulator::MoveLeftRightOrYaw( const int sign, const bool modifier )
{
    if ( modifier )
        MatrixRotateLocal<Matrix>( mMatrix, AXIS_YAW, sign * mYawRad );  // yaw left/right
    else
        MatrixTranslateLocal<Matrix>( mMatrix, AXIS_X, sign * -mStep );  // move left/right
}

/*****************************************************************************
 * Move forward/backward or pitch.
 *****************************************************************************/
void
KeyboardManipulator::MoveForwardBackwardOrPitch( const int sign, const bool modifier )
{
    if ( modifier )
        MatrixRotateLocal<Matrix>( mMatrix, AXIS_PITCH, sign * mPitchRad );  // pitch up/down
    else
        MatrixTranslateLocal<Matrix>( mMatrix, AXIS_Z, sign * mStep );  // move forward/backward
}

/*****************************************************************************
 * Move up/down.
 *****************************************************************************/
void
KeyboardManipulator::MoveUpDown( const int sign )
{
    MatrixTranslateLocal<Matrix>( mMatrix, AXIS_Y, sign * mStep );
}

/*****************************************************************************
 * Set manipulator's viewpoint.
 *****************************************************************************/
void
KeyboardManipulator::setByMatrix( const osg::Matrixd& matrix )
{
    mMatrix = matrix;
}

/*****************************************************************************
 * Get manipulator's viewpoint.
 *****************************************************************************/
osg::Matrixd
KeyboardManipulator::getMatrix( void ) const /*suppress checks.sh*/
{
    return mMatrix;
}

/*****************************************************************************
 * Set manipulator's viewpoint (inverted).
 *****************************************************************************/
void
KeyboardManipulator::setByInverseMatrix( const osg::Matrixd& inverseMatrix )
{
    setByMatrix( MatrixInvert( inverseMatrix ) );
}

/*****************************************************************************
 * Get manipulator's viewpoint (inverted).
 *****************************************************************************/
osg::Matrixd
KeyboardManipulator::getInverseMatrix( void ) const /*suppress checks.sh*/
{
    return MatrixInvert( mMatrix );
}

} // namespace view
