/*!
  \file
  \brief シミュレーション環境の描画

  \author Satofumi KAMIMURA

  $Id: DrawEnvironmentWidget.cpp 1424 2009-10-19 12:21:34Z satofumi $
*/

#include "DrawEnvironmentWidget.h"
#include "ModelManager.h"
#include "EventScheduler.h"
#include "CameraViewManager.h"
#include "gl_draw.h"
#include "Angle.h"
#include <QWheelEvent>

using namespace qrk;


namespace
{
  enum {
    MinimumWidth = 320,
    MinimumHeight = 240,
  };
}


struct DrawEnvironmentWidget::pImpl
{
  ModelManager model_manager_;
  DrawEnvironmentWidget* widget_;
  QColor clear_color_;
  CameraViewManager camera_view_;

  bool wireframe_;

  int width_;
  int height_;

  double x_;
  double y_;
  double z_;
  Angle x_axis_;
  Angle y_axis_;
  Angle z_axis_;


  pImpl(DrawEnvironmentWidget* widget)
    : widget_(widget), clear_color_(Qt::lightGray),
      wireframe_(false),
      width_(MinimumWidth), height_(MinimumHeight),
      x_(0.0), y_(0.0), z_(0.0)
  {
  }


  void initializeForm(void)
  {
    widget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  }


  void redraw(void)
  {
    double scale = camera_view_.scale();
    glScalef(scale, scale, scale);

    // 地面の描画
    gl_drawGround();

    const ModelManager::Models& models = model_manager_.models();
    //fprintf(stderr, "%d: ", models.size());
    for (ModelManager::Models::iterator it = models.begin();
         it != models.end(); ++it) {
      const ModelManager::model_t* model = *it;

      glPushMatrix();

      gl_setColor(model->color);
      switch (model->type) {

      case ModelManager::Box:
        //fprintf(stderr, "B, ");
        gl_moved(model->body_id);
        gl_drawBox(model->length, wireframe_);
        break;

      case ModelManager::Cylinder:
        //fprintf(stderr, "C, ");
        gl_moved(model->body_id);
        gl_drawCylinder(model->length, wireframe_);
        break;

      case ModelManager::Sphere:
        //fprintf(stderr, "S, ");
        gl_moved(model->body_id);
        gl_drawSphere(model->body_id, model->length, wireframe_);
        break;

      default:
        //fprintf(stderr, "[%d, %f, %x], ", model->type, model->type, model->type);
        break;
      }

      glPopMatrix();
    }
    //fprintf(stderr, "\n");
  }


  void update(void)
  {
    camera_view_.view(x_, y_, z_, x_axis_, y_axis_, z_axis_);

    resizeGL(width_, height_);
  }


  void resizeGL(int width, int height)
  {
    width_ = width;
    height_ = height;

    glViewport(0, 0, width, height);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    double aspect = 1.0 * width / height;
    gluPerspective(50.0, aspect, 0.1, 100.0);

    glTranslatef(-x_, -y_, -z_);
    glMatrixMode(GL_MODELVIEW);
  }
};


DrawEnvironmentWidget::DrawEnvironmentWidget(QWidget* parent)
  : QGLWidget(parent), pimpl(new pImpl(this))
{
  pimpl->initializeForm();
}


DrawEnvironmentWidget::~DrawEnvironmentWidget(void)
{
}


void DrawEnvironmentWidget::initializeGL(void)
{
  // !!! OpenGL を勉強してから、再度調整する

  qglClearColor(pimpl->clear_color_);

  const GLfloat lightPos[] = { 0.0, 0.0, 100.0, 1.0 };
  const GLfloat lightCol[] = { 1.0, 1.0, 1.0, 1.0 };
  const GLfloat no_color[] = { 0.0, 0.0, 0.0, 0.0 };

  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, lightCol);
  glLightfv(GL_LIGHT0, GL_SPECULAR, no_color);

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  glEnable(GL_CULL_FACE);
  glEnable(GL_DEPTH_TEST);
}


void DrawEnvironmentWidget::resizeGL(int width, int height)
{
  pimpl->resizeGL(width, height);
}


void DrawEnvironmentWidget::paintGL(void)
{
  static EventScheduler event_scheduler;
  event_scheduler.lock();

  qglClearColor(pimpl->clear_color_);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glLoadIdentity();

  if (pimpl->camera_view_.updated()) {
    pimpl->update();
  }

  glRotatef(pimpl->x_axis_.to_deg(), 1.0, 0.0, 0.0);
  glRotatef(pimpl->y_axis_.to_deg(), 0.0, 1.0, 0.0);
  glRotatef(pimpl->z_axis_.to_deg(), 0.0, 0.0, 1.0);

  pimpl->redraw();
  event_scheduler.unlock();
}


//void DrawEnvironmentWidget::mousePressEvent(QMouseEvent *event);
//void DrawEnvironmentWidget::mouseMoveEvent(QMouseEvent *event);


void DrawEnvironmentWidget::wheelEvent(QWheelEvent* event)
{
  int degrees = event->delta() / 8;
  int steps = degrees / 15;

  double scale = pimpl->camera_view_.scale();
  if (steps > 0) {
    scale *= 1.05;
  } else {
    scale *= 0.95;
  }
  pimpl->camera_view_.setScale(scale);
  event->accept();
}


QSize DrawEnvironmentWidget::minimumSizeHint(void) const
{
  return QSize(MinimumWidth, MinimumHeight);
}


void DrawEnvironmentWidget::setWireframeMode(bool on)
{
  pimpl->wireframe_ = on;
}


void DrawEnvironmentWidget::redraw(void)
{
  updateGL();
}
