// Description:
//   Block View smooth rotation
//
// Copyright (C) 2003 Frank Becker
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation;  either version 2 of the License,  or (at your option) any  later
// version.
//
// This program is distributed in the hope that it will be useful,  but  WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
//
#include <Config.hpp>
#include <GameState.hpp>
#include <BlockViewSmooth.hpp>

const float DEFAULT_ROTATION_SPEED = 5.0f;
const int DEFAULT_MOVE_STEPS = 10;

BlockViewSmooth::BlockViewSmooth( BlockModel &model):
    BlockViewBase(model),
    _rotationSpeed(DEFAULT_ROTATION_SPEED),
    _moveSteps(DEFAULT_MOVE_STEPS),
    _numSteps(0)
{
    XTRACE();
    resetRotations();

    ConfigS::instance()->getFloat( "rotationSpeed", _rotationSpeed);
    ConfigS::instance()->getInteger( "moveSteps", _moveSteps);
}

BlockViewSmooth::~BlockViewSmooth()
{
    XTRACE();
}

void BlockViewSmooth::resetRotations( void)
{
    _prevAxis = Point3D(1,0,0);
    _prevAngle = 0;
    _prevQ.set( _prevAngle, _prevAxis);

    _currentAxis = Point3D(1,0,0);
    _currentAngle = 0;
    _targetAngle = 0;

    _prev2Angle = _currentAngle;
}

void BlockViewSmooth::update( void)
{
    _prevOffset = _currentOffset;
    _prev2Angle = _currentAngle;

    if( (_currentAngle+_rotationSpeed)<_targetAngle) _currentAngle+=_rotationSpeed;
    else _currentAngle = _targetAngle; 

    if( _numSteps)
    {
	_currentOffset = _currentOffset + _delta;
	_numSteps--;
    }

    Point3Di &offset = _model.getBlockOffset();
    if( _oldOffset != offset)
    {
	_targetOffset = offset; // Point3D = Point3Di
	_oldOffset = offset;

	_delta = _targetOffset - _currentOffset;
	_delta = _delta / (float)_moveSteps;
	_numSteps = _moveSteps;
    }
}

void BlockViewSmooth::notifyNewBlock( void)
{
//    LOG_INFO << "New Block. Resetting rotations\n";
    resetRotations();

    _oldOffset = _model.getBlockOffset();
    _currentOffset = _oldOffset; // Point3D = Point3Di
}

#if 0

A sequence of rotations:
  Q(rotate origin to current orientation) * Q(rotate current to final orientation)
  t: partial rotation (angle*t) 0<t<1

1. Rotate request A->B
  Q(1) * Q(AB)t
2. at 'a' rotate request B->C
  Q(Aa) * Q(aBC)t = Q(Aa) * Q(aC)t
3. at 'b' rotate request C->D
  Q(Aab) * Q(bCD)t = Q(Ab) * Q(bD)t
 

A----a    B
      \
     __b
 __--    
D         C

#endif
//TODO: check if slerping is cheaper...

void BlockViewSmooth::notifyNewRotation( const Quaternion &q)
{
    Quaternion tmpQ;

    //convert the ongoing rotation into a quaternion: tmpQ = Q(ab)
    tmpQ.set( _currentAngle, _currentAxis);
    //combine rotations -> Q(Aab) = Q(Ab)
    //prevQ  = Q(Aa)
    _prevQ = tmpQ * _prevQ;
    //get the angle and axis for the new static rotation
    _prevQ.get( _prevAngle, _prevAxis);


    //convert the not yet executed portion of the rotation into a quaternion
    //tmpQ = Q(bC)
    tmpQ.set( _targetAngle-_currentAngle, _currentAxis);
    //set the new rotation Q(CD)
    _targetQ = q;
    //combine rotations -> Q(bCD) = Q(bD)
    _targetQ = _targetQ * tmpQ;
    //get the angle and axis for the new ongoing rotation
    _targetQ.get( _targetAngle, _currentAxis);

    //reset the current angle in the ongoing rotation
    _currentAngle = 0;
}

Point3D &BlockViewSmooth::getInterpolatedOffset( void)
{
    float gf = GameState::frameFraction;
    _interpOffset = _prevOffset + (_currentOffset-_prevOffset)*gf;
    return _interpOffset;
}
float BlockViewSmooth::getInterpolatedAngle( void)
{
    float gf = GameState::frameFraction;
    return _prev2Angle + (_currentAngle-_prev2Angle)*gf;
}
