//============================================================================
//
// KPendulum screen saver for KDE
// $Id: pendulum.h 311553 2004-05-15 06:05:30Z drenkhahn $
// Copyright (C) 2004 Georg Drenkhahn
//
// This file is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 2 as published by the
// Free Software Foundation.
//
//============================================================================

#ifndef __PENDULUM_H__
#define __PENDULUM_H__

// STL headers
#include <valarray>
// Qt headers
#include <qwidget.h>
#include <qtimer.h>
#include <qgl.h>
// GL headers
#include <GL/glu.h>
#include <GL/gl.h>
// KDE headers
#include <kscreensaver.h>

#include "vec3.h"
#include "rkodesolver.h"

// KPendulumSetupUi
#include "pendulumcfg.h"

//--------------------------------------------------------------------

/** @brief ODE solver for the Pendulum equations */
class PendulumOdeSolver : public RkOdeSolver<double>
{
  public:
   /** @brief Constuctor for the RK solver of the pendulum equation of motion
    * @param t initial time in seconds
    * @param dt initial time increment in seconds, just a hint for solver
    * @param y generalized coordinates of pendulum system
    * @param eps relative precision
    * @param m1 mass of upper pendulum
    * @param m2 mass of lower pendulum
    * @param l1 length of upper pendulum
    * @param l2 length of lower pendulum
    * @param g gravitational constant */
   PendulumOdeSolver(
      const double &t,
      const double &dt,
      std::valarray<double> &y,
      const double &eps,
      const double &m1,
      const double &m2,
      const double &l1,
      const double &l2,
      const double &g
      );

  protected:
   /** @brief ODE function for the pendulum equation of motion system
    * @param x time
    * @param y generalized coordinates of pendulum system
    * @return derivation dy/dx */
   std::valarray<double>
      f(const double &x, const std::valarray<double> &y) const;

  private:
   /** These private variables contain constants for faster numeric calculation.
    * They are derived from the constructor arguments m1,m2,l1,l2,g.  */
   const double A, B1, B, C, D, E, M;
};


//--------------------------------------------------------------------

/** @brief GL widget class for the KPendulum screen saver
 *
 * Class implements QGLWidget to display the KPendulum screen saver. */
class PendulumGLWidget : public QGLWidget
{
   Q_OBJECT;

  public:
   /** @brief Constructor of KPendulum's GL widget
    * @param parent parent widget, passed to QGLWidget's constructor
    * @param name name of widget, passed to QGLWidget's constructor */
   PendulumGLWidget(QWidget* parent=0, const char* name=0);
   /** @brief Destructor of KPendulum's GL widget */
   ~PendulumGLWidget(void);

   /** @brief Set phi angle of viewpoint
    * @param phi angle in sterad */
   void setEyePhi(double phi);
   /** @brief Set angles of pendulum configuration
    * @param q1 angle of 1. pendulum in sterad
    * @param q2 angle of 2. pendulum in sterad */
   void setAngles(const double& q1, const double& q2);
   /** @brief Set masses of pendulum configuration
    * @param m1 mass of 1. pendulum
    * @param m2 mass of 2. pendulum */
   void setMasses(const double& m1, const double& m2);
   /** @brief Set lengths of pendulum configuration
    * @param l1 length of 1. pendulum
    * @param l2 length of 2. pendulum */
   void setLengths(const double& l1, const double& l2);

   /* accessors for colour settings */

   /** @brief set color of the bars
    * @param c color */
   void setBarColor(const QColor& c);
   /** @brief get color of the bars
    * @return color */
   inline QColor barColor(void) const {return m_barColor;}
   /** @brief set color of mass 1
    * @param c color */
   void setM1Color(const QColor& c);
   /** @brief get color of mass 1
    * @return color */
   inline QColor m1Color(void) const {return m_m1Color;}
   /** @brief set color of mass 2
    * @param c color */
   void setM2Color(const QColor& c);
   /** @brief get color of mass 2
    * @return color */
   inline QColor m2Color(void) const {return m_m2Color;}

  protected:
   /** paint the GL view */
   virtual void paintGL();
   /** resize the gl view */
   virtual void resizeGL(int w, int h);
   /** setup the GL enviroment */
   virtual void initializeGL();

  private: // Private attributes
   /** Eye position distance from coordinate zero point */
   GLfloat eyeR;
   /** Eye position theta angle from z axis in sterad */
   double eyeTheta;
   /** Eye position phi angle (longitude) in sterad */
   double eyePhi;
   /** Light position distance from coordinate zero point */
   GLfloat lightR;
   /** Light position theta angle from z axis in sterad */
   double lightTheta;
   /** Light position phi angle (longitude) in sterad */
   double lightPhi;

   /** 1. pendulum's angle, degree */
   GLfloat ang1;
   /** 2. pendulum's angle, degree */
   GLfloat ang2;

   /** 1. pendulum's square root of mass */
   GLfloat sqrtm1;
   /** 2. pendulum's square root of mass */
   GLfloat sqrtm2;

   /** 1. pendulum's length */
   GLfloat l1;
   /** 2. pendulum's length */
   GLfloat l2;

   /** Pointer to a quadric object used in the rendering function paintGL() */
   GLUquadricObj* const quadM1;

   /** color of the pendulum bars */
   QColor m_barColor;
   /** color of the 1. mass */
   QColor m_m1Color;
   /** color of the 2. mass */
   QColor m_m2Color;
};

//--------------------------------------------------------------------

/** @brief Main class of the KPendulum screen saver
 *
 * This class implements KScreenSaver for the KPendulum screen saver. */
class KPendulumSaver : public KScreenSaver
{
   Q_OBJECT;

  public:
   /** @brief Constructor of the KPendulum screen saver object
    * @param drawable Id of the window in which the screen saver is drawed
    *
    * Initial settings are read from disk, the GL widget is set up and displayed
    * and the eq. of motion solver is started. */
   KPendulumSaver(WId drawable);
   /** @brief Destructor of the KPendulum screen saver object
    *
    * Only KPendulumSaver::solver is destoyed. */
   ~KPendulumSaver();
   /** read the saved settings from disk */
   void readSettings();
   /** init physical quantities, set up the GL area and (re)start the ode
    * solver.  Called if new parameters are specified in the setup dialog and at
    * startup. */
   void initData();

   /* accessors for PendulumGLWidget member variables */

   /** Set the displayed bar color of the pendulum */
   void setBarColor(const QColor& c);
   /** Get the displayed bar color of the pendulum */
   QColor barColor(void) const;

   static const QColor barColorDefault;

   /** Set the displayed color of the 1. pendulum mass */
   void setM1Color(const QColor& c);
   /** Get the displayed color of the 1. pendulum mass */
   QColor m1Color(void) const;

   static const QColor m1ColorDefault;

   /** Set the displayed color of the 2. pendulum mass */
   void setM2Color(const QColor& c);
   /** Get the displayed color of the 2. pendulum mass */
   QColor m2Color(void) const;

   static const QColor m2ColorDefault;

   /* accessors for own member variables */

   /** Set the mass ratio of the pendulum system. @sa
    * KPendulumSaver::m_massRatio */
   void setMassRatio(const double& massRatio);
   /** Get the mass ratio of the pendulum system. @sa
    * KPendulumSaver::m_massRatio */
   inline double massRatio(void) const {return m_massRatio;}

   // lower, upper limits (inclusive) and default values for the setup
   // parameters
   static const double massRatioLimitUpper;
   static const double massRatioLimitLower;
   static const double massRatioDefault;

   /** Set the length ratio of the pendulum system. @sa
    * KPendulumSaver::m_lengthRatio */
   void setLengthRatio(const double& lengthRatio);
   /** Get the length ratio of the pendulum system. @sa
    * KPendulumSaver::m_lengthRatio */
   inline double lengthRatio(void) const {return m_lengthRatio;}

   static const double lengthRatioLimitLower;
   static const double lengthRatioLimitUpper;
   static const double lengthRatioDefault;

   /** Set the gravitational constant. @sa KPendulumSaver::m_g */
   void setG(const double& g);
   /** Get the gravitational constant. @sa KPendulumSaver::m_g */
   inline double g(void) const {return m_g;}

   static const double gLimitLower;
   static const double gLimitUpper;
   static const double gDefault;

   /** Set the total energy. @sa KPendulumSaver::m_E */
   void setE(const double& E);
   /** Get the total energy. @sa KPendulumSaver::m_E */
   inline double E(void) const {return m_E;}

   static const double ELimitLower;
   static const double ELimitUpper;
   static const double EDefault;

   /** Set the time interval for the periodic perspective change. @sa
    * KPendulumSaver::m_persChangeInterval */
   void setPersChangeInterval(const unsigned int& persChangeInterval);
   /** Get the time interval for the periodic perspective change. @sa
    * KPendulumSaver::m_persChangeInterval */
   inline unsigned int persChangeInterval(void) const
      {return m_persChangeInterval;}

   static const unsigned int persChangeIntervalLimitLower =   5;
   static const unsigned int persChangeIntervalLimitUpper = 600;
   static const unsigned int persChangeIntervalDefault    =  15;

  public slots:
   /** slot is called if integration should proceed by ::deltaT */
   void doTimeStep();
   /** slot is called if setup dialog changes in size and the GL are should be
    * adjusted */
   void resizeGlArea(QResizeEvent* e);

  private:
   /** The ode solver which is used to integrate the equations of motion */
   PendulumOdeSolver* solver;
   /** Gl widget of simulation */
   PendulumGLWidget*  glArea;
   /** Timer for the real time integration of the eqs. of motion */
   QTimer*            timer;

   /** Time step size for the integration in milliseconds.  20 ms corresponds to
    * a frame rate of 50 fps. */
   static const unsigned int deltaT = 20;
   static const double eyePhiDefault;

   // saved settings

   /** Mass ratio m2/(m1+m2) of the pendulum masses.  Value is determined by the
    * setup dialog.  Variable is accessed by setMassRatio() and massRatio(). */
   double       m_massRatio;
   /** Length ratio l2/(l1+l2) of the pendulums.  Value is determined by the
    * setup dialog.  Variable is accessed by setLengthRatio() and
    * lengthRatio(). */
   double       m_lengthRatio;
   /** Gravitational constant (in arbitrary units).  Value is determined by the
    * setup dialog.  Variable is accessed by setG() and g(). */
   double       m_g;
   /** Total energy of the system in units of the maximum possible potential
    * energy.  Value is determined by the setup dialog.  Variable is accessed by
    * setE() and E(). */
   double       m_E;
   /** Time interval after which a new perspective changed happens.  Value is
    * determined by the setup dialog.  Variable is accessed by
    * setPersChangeInterval() and persChangeInterval(). */
   unsigned int m_persChangeInterval;
};

//--------------------------------------------------------------------

/** @brief KPendulum screen saver setup dialog.
 *
 * This class handles the KPendulum screen saver setup dialog. */
class KPendulumSetup : public KPendulumSetupUi
{
   Q_OBJECT;

  public:
   /** @brief Constructor for the KPendulum screen saver setup dialog
    * @param parent Pointer to the parent widget, passed to KPendulumSetupUi
    * @param name Widget name
    *
    * The dialog box is set up and the screen saver object KPendulumSetup::saver
    * is instantiated. */
   KPendulumSetup(QWidget* parent = 0, const char* name = 0);
   /** @brief Destructor of the KPendulum screen saver setup dialog
    *
    * Only KPendulumSetup::saver is deleted. */
   ~KPendulumSetup(void);

  public slots:
   /** slot for the "OK" button: save settings and exit */
   void okButtonClickedSlot(void);
   /** slot for the "About" button: show the About dialog */
   void aboutButtonClickedSlot(void);

   /** slot is called if the mass ratio edit field looses its focus.  If the
    * input is acceptable KPendulumSaver::setMassRatio() is called. */
   void mEditLostFocusSlot(void);
   /** slot is called if the length ratio edit field looses its focus.  If the
    * input is acceptable KPendulumSaver::setLengthRatio() is called. */
   void lEditLostFocusSlot(void);
   /** slot is called if the gravitational constant edit field looses its focus.
    * If the input is acceptable KPendulumSaver::setG() is called. */
   void gEditLostFocusSlot(void);
   /** slot is called if the energy edit field looses its focus.  If the input
    * is acceptable KPendulumSaver::setE() is called. */
   void eEditLostFocusSlot(void);
   /** slot is called if the perspective change interval spin box changed.  If
    * the input is acceptable KPendulumSaver::setPersChangeInterval() is
    * called. */
   void persChangeEnteredSlot(int t);

   /** slot is called if the bar color button was clicked.  A color dialog is
    * opened and the result is given to KPendulumSaver::setBarColor(). */
   void barColorButtonClickedSlot(void);
   /** slot is called if the mass 1 color button was clicked.  A color dialog is
    * opened and the result is given to KPendulumSaver::setM1Color(). */
   void m1ColorButtonClickedSlot(void);
   /** slot is called if the mass 2 color button was clicked.  A color dialog is
    * opened and the result is given to KPendulumSaver::setM2Color(). */
   void m2ColorButtonClickedSlot(void);

  private:
   /** Pointer to the screen saver object.  Its member KPendulumSaver::glArea is
    * displayed in the preview area */
   KPendulumSaver* saver;
};

#endif
