/***************************************************************************
 *   Copyright (C) 2004 by Raphael Langerhorst                             *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#include "GCamera.h"

#include <qapplication.h>

using namespace GCS;

namespace GCE
{

GCamera::GCamera(GOpenGLFrame* frame, GCS::GForm* form, QObject* parent, const char* name)
: QObject(parent,name),
  KeyUpdateTime(QTime::currentTime()),
  Frame(frame),
  Form(form),
  ViewDistancePercent(250),
  SpeedFactor(200),
  speedx(0),
  speedy(0),
  shutdown(true),
  up_key_down(false),
  down_key_down(false),
  left_key_down(false),
  right_key_down(false),
  space_key_down(false)
{
  qApp->installEventFilter(this);
  connect(qApp,SIGNAL(aboutToQuit()),this,SLOT(stopCamera()));
  
  if (Frame)
  {
    connect(Frame,SIGNAL(beforeRendering()),this,SLOT(update()));
  }
}


GCamera::~GCamera()
{
}

void GCamera::update()
{
  if (!shutdown)
  {
//     qDebug("updating camera");
    double delta_t = KeyUpdateTime.restart()*0.001;
    //update speeds:
    if (up_key_down)
      speedy += delta_t;
    if (down_key_down)
      speedy -= delta_t;
    if (left_key_down)
      speedx += delta_t;
    if (right_key_down)
      speedx -= delta_t;
      
  
    //update position from form:
    GVector3& pos = Frame->ViewProperties.ViewPosition;
    GVector3& target = Frame->ViewProperties.ViewTarget;
    GVector3& up = Frame->ViewProperties.ViewUp;
    
//     qDebug(QString("Current form position before updating: %1").arg(Form->Position.toString()));
    
    GVector3 dir = target - pos;
    
    if (dir.length()==0)
      dir.z = 1;
    
    target.set(Form->Position);
    dir.normalize();
    
    if (space_key_down)
    {
      emit this->translationSpeedImpulse(dir*(delta_t*SpeedFactor));
//       Form->Dynamics.Speed += dir*(delta_t*300);
    }
    
    dir.mul(Form->getRadiusMax()*this->ViewDistancePercent *0.01 );
    pos = target - dir;
    
//     qDebug(QString("Current form position after updating: %1").arg(Form->Position.toString()));
    
    if (speedx != 0 || speedy != 0)
    {
    //@todo: don't modify the view properties directly if there is a camera element set, probably we should give the camera a pointer to the GMoveAgent that it should use for speed and rotation impulses!
//       bool x_positive;
//       bool y_positive;
      
      if (dir.x != 0 || dir.y != 0 || dir.z != 0)
      {
        
        dir.turnAroundAxis(up,speedx);
        
        GVector3 side = dir.cross(up);
        
        side.normalize();
        
        dir.turnAroundAxis(side,speedy);
        
        pos = target - dir;
        
        up = side.cross(dir);
        
        up.normalize();
  
        double factor = 0.99;
        double abs_sub = 0.001;
        double border = 0.001;
                
        speedx *= factor*delta_t;
        speedy *= factor*delta_t;
        
        if (speedx > 0)
        {
          speedx -= abs_sub*delta_t;
          if (speedx < border)
            speedx = 0;
        }
        else if (speedx<0)
        {
          speedx += abs_sub*delta_t;
          if (speedx > -border)
            speedx = 0;
        }
        
        if (speedy > 0)
        {
          speedy -= abs_sub*delta_t;
          if (speedy < border)
            speedy = 0;
        }
        else if (speedy<0)
        {
          speedy += abs_sub*delta_t;
          if (speedy > -border)
            speedy = 0;
        }
      }
      
    }
  }
}

void GCamera::startCamera()
{
  Q_CHECK_PTR(Frame);
  Q_CHECK_PTR(Form);
  
  if (Frame != NULL && Form != NULL)
  {
    this->shutdown = false;
    this->KeyUpdateTime = QTime::currentTime();
  }
}

void GCamera::stopCamera()
{
  this->shutdown = true;
}

void GCamera::setFrame(GOpenGLFrame* frame)
{
  if (Frame)
  {
    disconnect(Frame,SIGNAL(beforeRendering()),this,SLOT(update()));
  }
  
  this->Frame = frame;
  
  if (Frame)
  {
    connect(Frame,SIGNAL(beforeRendering()),this,SLOT(update()));
  }
}

void GCamera::setForm(GCS::GForm* form)
{
  this->Form = form;
}

bool GCamera::eventFilter(QObject* watched, QEvent* e)
{
  if (Frame)
  {
    if (!Frame->hasFocus())
      return FALSE;
  }
  if (e->type() == QEvent::KeyPress)
  {
    QKeyEvent* ke = (QKeyEvent*)(e);
    int key = ke->key();
    
//     //@todo determine correct elapsed time (save key states and check which ones are down in the main thread, etc...)
//     double delta = 0.01;
    
    switch(key)
    {
      case Qt::Key_Left:
        left_key_down = true;
        break;
      case Qt::Key_Right:
        right_key_down = true;
        break;
      case Qt::Key_Up:
        up_key_down = true;
        break;
      case Qt::Key_Down:
        down_key_down = true;
        break;
      case Qt::Key_Space:
        {
          space_key_down = true;
          break;
        }
      case Qt::Key_Return:
        {
//           Form->Dynamics.Speed.set(0,0,0);
          emit stopMovement();
          break;
        }
      case Qt::Key_Enter:
        {
          emit stopMovement();
//           Form->Dynamics.Speed.set(0,0,0);
          break;
        }
      default:
        qDebug("unknown key");
        return FALSE;
    }
    //if we get here the key press event has been processed:
//     e->accept();
    return TRUE;
  }
  else if (e->type() == QEvent::KeyRelease)
  {
    QKeyEvent* ke = (QKeyEvent*)(e);
    int key = ke->key();
    
//     //@todo determine correct elapsed time (save key states and check which ones are down in the main thread, etc...)
//     double delta = 0.01;
    
    switch(key)
    {
      case Qt::Key_Left:
        left_key_down = false;
        break;
      case Qt::Key_Right:
        right_key_down = false;
        break;
      case Qt::Key_Up:
        up_key_down = false;
        break;
      case Qt::Key_Down:
        down_key_down = false;
        break;
      case Qt::Key_Space:
        {
          space_key_down = false;
          break;
        }
    }
    //if we get here the key press event has been processed:
//     e->accept();
    return TRUE;
  }
  
  return false;
}

void GCamera::setViewDistance(int new_distance_percent)
{
  if (new_distance_percent <= 0)
    new_distance_percent = 1;
  this->ViewDistancePercent = new_distance_percent;
}

void GCamera::setSpeedFactor(double speed_factor)
{
  this->SpeedFactor = speed_factor;
}

};


syntax highlighted by Code2HTML, v. 0.9.1