#include <GL/glew.h>
#include "Window.h"

#include <vector>
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>

#include <QDockWidget>
#include <QGroupBox>
#include <QAction>
#include <QButtonGroup>
#include <QMenuBar>
#include <QApplication>
#include <QLayout>
#include <QLabel>
#include <QProgressBar>
#include <QCheckBox>
#include <QRadioButton>
#include <QColorDialog>
#include <QLCDNumber>
#include <QPixmap>
#include <QFrame>
#include <QSplitter>
#include <QMenu>
#include <QScrollArea>
#include <QCoreApplication>
#include <QFont>
#include <QSizePolicy>
#include <QImageReader>
#include <QStatusBar>
#include <QPushButton>
#include <QMessageBox>
#include <QFileDialog>
#include <QStatusBar>
#include <QScrollArea>

#include "RayTracer.h"
#include "Object.h"

using namespace std;


Window::Window () :
    QMainWindow (0),
    selectedObj(0),
    mat(0)
{
    try {
        viewer = new GLViewer(this);
    } catch (GLViewer::Exception e) {
        cerr << e.getMessage () << endl;
        exit (1);
    }
    setCentralWidget (viewer);

    addDockWidget(Qt::RightDockWidgetArea, initControlDock());
    addDockWidget(Qt::LeftDockWidgetArea, initRenderControlDock());

    parameters.dof.quality = 2;
    parameters.dof.aperture = 2.8f;
    parameters.dof.focusDistance = 1.0f;
    statusBar()->showMessage("");
    setSelectedObject(0);
}

Window::~Window () {

}

void Window::renderRayImage ()
{
    SRenderParameters rp = initRenderParameters();
    QImage image(QSize(rp.screen.width, rp.screen.height), QImage::Format_RGB32);
    QTime timer;

    timer.start ();
    RayTracer::getInstance()->render(image, rp, this);
    int elapsed = timer.elapsed();

    viewer->setRayImage(image);
    statusBar()->showMessage(QString ("Raytracing performed in ")
                             + QString::number (elapsed)
                             + QString ("ms at ")
                             + QString::number (rp.screen.width) + QString ("x") + QString::number (rp.screen.height)
                             + QString (" screen resolution"));
    viewer->setDisplayMode (GLViewer::RayDisplayMode);
    qApp->alert(this);
}

void Window::renderDofPreview()
{
    SRenderParameters rp = initRenderParameters();
    QImage image(QSize(rp.screen.width, rp.screen.height), QImage::Format_RGB32);

    RayTracer::getInstance()->renderDofPreview(image, rp);

    viewer->setRayImage(image);
    viewer->setDisplayMode (GLViewer::RayDisplayMode);
    qApp->alert(this);
}

void Window::gpuContinuousRender()
{
    printf("GPU Render: not implemented\n");
}

SRenderParameters Window::initRenderParameters()
{
    qglviewer::Camera * cam = viewer->camera ();
    qglviewer::Vec p = cam->position ();
    qglviewer::Vec d = cam->viewDirection ();
    qglviewer::Vec u = cam->upVector ();
    qglviewer::Vec r = cam->rightVector ();

    Vec3Df camPos (p[0], p[1], p[2]);
    Vec3Df viewDirection (d[0], d[1], d[2]);
    Vec3Df upVector (u[0], u[1], u[2]);
    Vec3Df rightVector (r[0], r[1], r[2]);

    SRenderParameters rp;
    rp.cam.dir = viewDirection;
    rp.cam.pos = camPos;
    rp.cam.right = rightVector;
    rp.cam.up = -upVector;
    rp.cam.aspect = cam->aspectRatio ();
    rp.cam.fov = cam->fieldOfView ();
    rp.screen.width = cam->screenWidth ();
    rp.screen.height = cam->screenHeight ();
    rp.screen.aaX = parameters.screen.aaX;
    rp.screen.aaY = parameters.screen.aaY;
    rp.screen.aaEnabled = parameters.screen.aaEnabled;
    rp.dof.quality = parameters.dof.quality;
    rp.dof.focusDistance = parameters.dof.focusDistance;
    rp.dof.aperture = parameters.dof.aperture;
    rp.dof.enabled = parameters.dof.enabled;

    rp.cam.up.normalize();
    rp.cam.right.normalize();
    rp.cam.dir.normalize();

    return rp;
}

void Window::setFOV (int b)
{
    float fov = std::atan( 0.0432666153f / (0.002*b) );
    viewer->camera()->setFieldOfView(fov);
    viewer->updateGL();
}

void Window::enableDoF(bool e)
{
    parameters.dof.enabled = e;
}

void Window::enableAntiAliasing(bool e)
{
    parameters.screen.aaEnabled = e;
}

void Window::setAAX(int v)
{
    parameters.screen.aaX = v;
}

void Window::setAAY(int v)
{
    parameters.screen.aaY = v;
}

void Window::enablePathTracing(bool e)
{
    RayTracer::getInstance()->usePathTracing(e);
}

void Window::setBGColor ()
{
    QColor c = QColorDialog::getColor (QColor (133, 152, 181), this);
    if (c.isValid () == true)
    {
        RayTracer::getInstance ()->setBackgroundColor(c);
        viewer->setBackgroundColor(c);
        viewer->updateGL();
    }
}

void Window::setDofQuality (int b) {
    parameters.dof.quality = b;
}

void Window::setDofAperture (int b) {
    parameters.dof.aperture = b;
}

void Window::setFocusDistance (int b) {
    parameters.dof.focusDistance = 0.1f+b*0.01f;
    focusLabel->setText(QString::number(0.1f + b * 0.01f));
}

void Window::toggleClickFocus()
{
    if(viewer->getClickMode() != GLViewer::Move)
        viewer->setClickMode(GLViewer::Move);
    else
        viewer->setClickMode(GLViewer::Focus);
}

void Window::setClickFocusDistance (int x, int y) {
    IntersectionResult res;
    Ray viewRay;
    qglviewer::Camera * cam = viewer->camera ();
    qglviewer::Vec p = cam->position ();
    qglviewer::Vec d = cam->viewDirection ();
    qglviewer::Vec u = cam->upVector ();
    qglviewer::Vec r = cam->rightVector ();
    Vec3Df camPos (p[0], p[1], p[2]);
    Vec3Df camDirection (d[0], d[1], d[2]);
    Vec3Df upVector (u[0], u[1], u[2]);
    Vec3Df rightVector (r[0], r[1], r[2]);
    float fieldOfView = cam->fieldOfView ();
    unsigned int screenWidth = cam->screenWidth ();
    unsigned int screenHeight = cam->screenHeight ();
    float aspectRatio = cam->aspectRatio ();
    Vec3Df camDir = camDirection;
    Vec3Df camUp = -upVector;
    Vec3Df camRight = rightVector;
    camDir.normalize();
    camUp.normalize();
    camRight.normalize();
    viewRay.setOrigin(camPos);
    float nearSizeY = 2 * tan(fieldOfView / 2);
    float nearSizeX = nearSizeY * aspectRatio;
    float xRatio = x / float(screenWidth);
    float yRatio = y / float(screenHeight);
    Vec3Df nearOrigin = camDir + camUp * nearSizeY / 2.0f - camRight * nearSizeX / 2.0f;
    Vec3Df rayDir = nearOrigin + xRatio * nearSizeX * camRight - yRatio * nearSizeY * camUp;
    viewRay.setDirection(rayDir);


    Scene::getInstance()->intersect(viewRay, res);


    parameters.dof.focusDistance = Vec3Df::dotProduct((res.i.point - camPos), camDir);
    focusLabel->setText(QString::number(Vec3Df::dotProduct((res.i.point - camPos), camDir)));
    focus->setValue(std::floor(100*(Vec3Df::dotProduct((res.i.point - camPos), camDir)-0.1f)));
    viewer->setClickMode(GLViewer::Move);
}

void Window::showRayImage () {
    viewer->setDisplayMode (GLViewer::RayDisplayMode);
}

void Window::exportGLImage () {
    viewer->saveSnapshot (false, false);
}

void Window::exportRayImage ()
{
    viewer->setDisplayMode (GLViewer::RayDisplayMode);
    exportGLImage();
    /*
    QString filename = QFileDialog::getSaveFileName (this,
                                                     "Save ray-traced image",
                                                     ".",
                                                     "*.jpg *.bmp *.png");
    if (!filename.isNull () && !filename.isEmpty ())
      viewer->getRayImage().save (filename);
    */
}

void Window::about () {
    QMessageBox::about (this,
                        "About This Program",
                        "<b>RayMini</b><br> by <i>Tamy Boubekeur</i>.<br>"
                        "Raytracing engine made by:<br>"
                        " - <i>Thibault Lescoat</i><br>"
                        " - <i>Florent Charraire</i><br>"
                        " - <i>David Beer-Gabel</i>");
}

QDockWidget* Window::initControlDock()
{
    QDockWidget* controlDockWidget = new QDockWidget (this);
    QGroupBox* controlWidget = new QGroupBox ();
    QVBoxLayout * layout = new QVBoxLayout (controlWidget);

    // ---------------------------------
    // Preview group box
    // ---------------------------------
    QGroupBox * previewGroupBox = new QGroupBox ("Preview", controlWidget);
    QVBoxLayout * previewLayout = new QVBoxLayout (previewGroupBox);

    QCheckBox* showLightsCB = new QCheckBox ("Show lights");
    connect (showLightsCB, SIGNAL (toggled (bool)), viewer, SLOT (showSceneLights (bool)));
    previewLayout->addWidget (showLightsCB);

    QCheckBox* wireframeCheckBox = new QCheckBox ("Wireframe", previewGroupBox);
    wireframeCheckBox->setShortcut(Qt::CTRL + Qt::Key_W);
    wireframeCheckBox->setToolTip("Toggle wireframe <i>Ctrl + W</i>");
    connect (wireframeCheckBox, SIGNAL (toggled (bool)), viewer, SLOT (setWireframe (bool)));
    previewLayout->addWidget (wireframeCheckBox);

    QButtonGroup * modeButtonGroup = new QButtonGroup (previewGroupBox);
    modeButtonGroup->setExclusive (true);
    QRadioButton* flatButton = new QRadioButton ("Flat", previewGroupBox);
    flatButton->setShortcut(Qt::CTRL + Qt::Key_F);
    flatButton->setToolTip("Flat normals <i>Ctrl + F</i>");
    modeButtonGroup->addButton (flatButton, static_cast<int>(GLViewer::Flat));
    QRadioButton* smoothButton = new QRadioButton ("Smooth", previewGroupBox);
    smoothButton->setShortcut(Qt::CTRL + Qt::Key_G);
    smoothButton->setToolTip("Smooth normals <i>Ctrl + G</i>");
    smoothButton->setChecked(true);
    modeButtonGroup->addButton (smoothButton, static_cast<int>(GLViewer::Smooth));
    connect (modeButtonGroup, SIGNAL (buttonClicked (int)), viewer, SLOT (setRenderingMode (int)));
    previewLayout->addWidget (flatButton);
    previewLayout->addWidget (smoothButton);

    QPushButton * snapshotButton  = new QPushButton ("Save preview", previewGroupBox);
    snapshotButton->setShortcut(Qt::CTRL + Qt::Key_S);
    snapshotButton->setToolTip("Save viewed image <i>Ctrl + S</i>");
    connect (snapshotButton, SIGNAL (clicked ()) , this, SLOT (exportGLImage ()));
    previewLayout->addWidget (snapshotButton);

    layout->addWidget (previewGroupBox);

    // ---------------------------------
    // Render group box
    // ---------------------------------
    QGroupBox * rayGroupBox = new QGroupBox ("Ray Tracing", controlWidget);
    QVBoxLayout * rayLayout = new QVBoxLayout (rayGroupBox);

    QCheckBox* pathTracing = new QCheckBox("Use pathtracing");
    connect(pathTracing, SIGNAL(toggled(bool)), this, SLOT(enablePathTracing(bool)));
    rayLayout->addWidget(pathTracing);

    QPushButton * rayButton = new QPushButton ("Render", rayGroupBox);
    rayButton->setShortcut(Qt::Key_R);
    rayButton->setToolTip("Render with raytracer <i>R</i>");
    connect (rayButton, SIGNAL (clicked ()), this, SLOT (renderRayImage ()));
    rayLayout->addWidget (rayButton);

    QPushButton * continuousRayButton = new QPushButton ("CUDA render", rayGroupBox);
    continuousRayButton->setToolTip("Continuous render with CUDA");
    connect (continuousRayButton, SIGNAL (clicked ()), this, SLOT (gpuContinuousRender()));
    rayLayout->addWidget (continuousRayButton);

    QPushButton * showButton = new QPushButton ("Show", rayGroupBox);
    showButton->setShortcut(Qt::Key_S);
    showButton->setToolTip("Show last rendered image<i>S</i>");
    rayLayout->addWidget (showButton);
    connect (showButton, SIGNAL (clicked ()), this, SLOT (showRayImage ()));
    QPushButton * saveButton  = new QPushButton ("Save", rayGroupBox);
    saveButton->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_S);
    saveButton->setToolTip("Save rendered image <i>Ctrl + Shift + S</i>");
    connect (saveButton, SIGNAL (clicked ()) , this, SLOT (exportRayImage ()));
    rayLayout->addWidget (saveButton);

    layout->addWidget (rayGroupBox);

    // ---------------------------------
    // Global settings group box
    // ---------------------------------
    QGroupBox * globalGroupBox = new QGroupBox ("Global Settings", controlWidget);
    QVBoxLayout * globalLayout = new QVBoxLayout (globalGroupBox);

    QPushButton * aboutButton  = new QPushButton ("About", globalGroupBox);
    connect (aboutButton, SIGNAL (clicked()) , this, SLOT (about()));
    globalLayout->addWidget (aboutButton);

    QPushButton * quitButton  = new QPushButton ("Quit", globalGroupBox);
    connect (quitButton, SIGNAL (clicked()) , qApp, SLOT (closeAllWindows()));
    quitButton->setShortcut(Qt::Key_Escape);
    quitButton->setToolTip("Quit application <i>Esc</i>");
    globalLayout->addWidget (quitButton);

    layout->addWidget (globalGroupBox);

    layout->addStretch (0);
    controlDockWidget->setWidget(controlWidget);
    controlDockWidget->adjustSize ();
    controlDockWidget->setFeatures (QDockWidget::AllDockWidgetFeatures);
    return controlDockWidget;
}

QDockWidget* Window::initRenderControlDock()
{
	QGroupBox* controlWidget = new QGroupBox();
    QVBoxLayout * layout = new QVBoxLayout (controlWidget);

    // ---------------------------------
    // Depth of field group box
    // ---------------------------------
    QGroupBox * dofGroupBox = new QGroupBox ("Depth of Field", controlWidget);
    QVBoxLayout* dofLayout = new QVBoxLayout (dofGroupBox);
    dofGroupBox->setCheckable(true);
    dofGroupBox->setChecked(false);
    connect(dofGroupBox, SIGNAL(toggled(bool)), this, SLOT(enableDoF(bool)));

    dofLayout->addWidget (new QLabel("Quality"));
    QHBoxLayout * dofQualityLayout = new QHBoxLayout (controlWidget);
    QSlider * dofQuality = new QSlider(Qt::Horizontal);
    dofQuality->setTickPosition(QSlider::TicksBelow);
    dofQuality->setTickInterval(10);
    dofQuality->setSingleStep(1);
    dofQuality->setRange(1,10);
    dofQuality->setValue(parameters.dof.quality);
    dofQualityLayout->addWidget (dofQuality);
    connect (dofQuality, SIGNAL (valueChanged(int)), this, SLOT (setDofQuality(int)));
    QLabel * dofQualityLabel = new QLabel("1",controlWidget);
    dofQualityLabel->setFixedWidth(25);
    dofQualityLabel->setAlignment(Qt::AlignCenter);
    dofQualityLayout->addWidget (dofQualityLabel);
    connect (dofQuality, SIGNAL (valueChanged(int)), dofQualityLabel, SLOT (setNum(int)));
    dofLayout->addLayout(dofQualityLayout);

    QHBoxLayout * apertureLayout = new QHBoxLayout (controlWidget);
    QSlider * aperture = new QSlider(Qt::Horizontal);
    aperture->setTickPosition(QSlider::TicksBelow);
    aperture->setTickInterval(10);
    aperture->setSingleStep(1);
    aperture->setRange(1,32);
    aperture->setValue(parameters.dof.aperture);
    apertureLayout->addWidget (aperture);
    connect (aperture, SIGNAL (valueChanged(int)), this, SLOT (setDofAperture(int)));
    QLabel * apertureTextLabel = new QLabel("Aperture",controlWidget);
    dofLayout->addWidget (apertureTextLabel);
    QLabel * apertureLabel = new QLabel("1",controlWidget);
    apertureLabel->setFixedWidth(25);
    apertureLabel->setAlignment(Qt::AlignCenter);
    apertureLayout->addWidget (apertureLabel);
    connect (aperture, SIGNAL (valueChanged(int)), apertureLabel, SLOT (setNum(int)));
    dofLayout->addLayout(apertureLayout);

    QHBoxLayout * focalLayout = new QHBoxLayout (controlWidget);
    QSlider * focal = new QSlider(Qt::Horizontal);
    focal->setTickPosition(QSlider::TicksBelow);
    focal->setTickInterval(10);
    focal->setSingleStep(1);
    focal->setRange(24,120);
    focalLayout->addWidget (focal);
    connect (focal, SIGNAL (valueChanged(int)), this, SLOT (setFOV(int)));
    QLabel * focalTextLabel = new QLabel("Focal Length (mm)",controlWidget);
    dofLayout->addWidget (focalTextLabel);
    QLabel * focalLabel = new QLabel("24",controlWidget);
    focalLabel->setFixedWidth(25);
    focalLabel->setAlignment(Qt::AlignCenter);
    focalLayout->addWidget (focalLabel);
    connect (focal, SIGNAL (valueChanged(int)), focalLabel, SLOT (setNum(int)));
    dofLayout->addLayout(focalLayout);

    QHBoxLayout * mfocusLayout = new QHBoxLayout (controlWidget);
    focus = new QSlider(Qt::Horizontal);
    focus->setTickPosition(QSlider::TicksBelow);
    focus->setTickInterval(10);
    focus->setSingleStep(1);
    focus->setRange(0,1000);
    focus->setValue(parameters.dof.focusDistance);
    mfocusLayout->addWidget (focus);
    connect (focus, SIGNAL (valueChanged(int)), this, SLOT (setFocusDistance(int)));
    QLabel * mfocusTextLabel = new QLabel("Manual Focus (m)",controlWidget);
    dofLayout->addWidget (mfocusTextLabel);
    focusLabel = new QLabel("1",controlWidget);
    focusLabel->setFixedWidth(25);
    focusLabel->setAlignment(Qt::AlignCenter);
    mfocusLayout->addWidget (focusLabel);
    dofLayout->addLayout(mfocusLayout);

    dofButton = new QPushButton ("Set Focus Point", dofGroupBox);
    dofButton->setShortcut(Qt::Key_F);
    dofButton->setCheckable(true);
    dofButton->setToolTip("Click to set the point of focus <i>F</i>");
    dofLayout->addWidget (dofButton);
    connect (dofButton, SIGNAL (clicked ()), this, SLOT (toggleClickFocus()));

    QPushButton * dofPreviewButton = new QPushButton ("Preview", dofGroupBox);
    dofPreviewButton->setToolTip("Preview the dof settings");
    dofLayout->addWidget (dofPreviewButton);
    connect (dofPreviewButton, SIGNAL (clicked ()), this, SLOT (renderDofPreview()));

    layout->addWidget (dofGroupBox);

    // ---------------------------------
    // Anti-aliasing group box
    // ---------------------------------
    QGroupBox * aaGroupBox = new QGroupBox ("Anti-aliasing");
    QVBoxLayout* aaLayout = new QVBoxLayout (aaGroupBox);
    aaGroupBox->setCheckable(true);
    aaGroupBox->setChecked(false);
    connect(aaGroupBox, SIGNAL(toggled(bool)), this, SLOT(enableAntiAliasing(bool)));

    QHBoxLayout * aaXLayout = new QHBoxLayout (controlWidget);
    aaXLayout->addWidget(new QLabel("Resolution X: "));
    QSpinBox* aaXSpin = new QSpinBox();
    aaXSpin->setRange(1, 128);
    aaXSpin->setValue(1);
    connect(aaXSpin, SIGNAL(valueChanged(int)), this, SLOT(setAAX(int)));
    aaXLayout->addWidget(aaXSpin);
    aaLayout->addLayout(aaXLayout);

    QHBoxLayout * aaYLayout = new QHBoxLayout (controlWidget);
    aaYLayout->addWidget(new QLabel("Resolution Y: "));
    QSpinBox* aaYSpin = new QSpinBox();
    aaYSpin->setRange(1, 128);
    aaYSpin->setValue(1);
    connect(aaYSpin, SIGNAL(valueChanged(int)), this, SLOT(setAAY(int)));
    aaYLayout->addWidget(aaYSpin);
    aaLayout->addLayout(aaYLayout);

    layout->addWidget(aaGroupBox);

    // ---------------------------------
    // Object material
    // ---------------------------------
    QGroupBox * objGroupBox = new QGroupBox ("Material");
    QVBoxLayout* objLayout = new QVBoxLayout (objGroupBox);

    objButton = new QPushButton ("Select object");
    objButton->setCheckable(true);
    objLayout->addWidget (objButton);
    connect (objButton, SIGNAL (clicked ()), this, SLOT (selectObject()));

    QHBoxLayout* hLayout = new QHBoxLayout();
    diffuseSpin = new QDoubleSpinBox();
    diffuseSpin->setRange(0.0,1.0);
    diffuseSpin->setSingleStep(0.01);
    connect(diffuseSpin, SIGNAL(valueChanged(double)), this, SLOT(materialDiffuseChanged(double)));
    hLayout->addWidget(new QLabel("Diffuse: "));
    hLayout->addWidget(diffuseSpin);
    objLayout->addLayout(hLayout);

    hLayout = new QHBoxLayout();
    specularSpin = new QDoubleSpinBox();
    specularSpin->setRange(0.0,1.0);
    specularSpin->setSingleStep(0.01);
    connect(specularSpin, SIGNAL(valueChanged(double)), this, SLOT(materialSpecularChanged(double)));
    hLayout->addWidget(new QLabel("Specular: "));
    hLayout->addWidget(specularSpin);
    objLayout->addLayout(hLayout);

    hLayout = new QHBoxLayout();
    shininessSpin = new QSpinBox();
    shininessSpin->setRange(0, 128);
    shininessSpin->setSingleStep(2);
    connect(shininessSpin, SIGNAL(valueChanged(int)), this, SLOT(materialShininessChanged(int)));
    hLayout->addWidget(new QLabel("Shininess: "));
    hLayout->addWidget(shininessSpin);
    objLayout->addLayout(hLayout);

    hLayout = new QHBoxLayout();
    transparentSpin = new QDoubleSpinBox();
    transparentSpin->setRange(0.0,1.0);
    transparentSpin->setSingleStep(0.01);
    connect(transparentSpin, SIGNAL(valueChanged(double)), this, SLOT(materialTransparencyChanged(double)));
    hLayout->addWidget(new QLabel("Transparency: "));
    hLayout->addWidget(transparentSpin);
    objLayout->addLayout(hLayout);

    pureMirrorCB = new QCheckBox("Pure mirror");
    connect(pureMirrorCB, SIGNAL(toggled(bool)), this, SLOT(materialPureMirrorChanged(bool)));
    objLayout->addWidget(pureMirrorCB);

    hLayout = new QHBoxLayout();
    indiceSpin = new QDoubleSpinBox();
    indiceSpin->setRange(1.0, 10000);
    indiceSpin->setSingleStep(0.1);
    connect(indiceSpin, SIGNAL(valueChanged(double)), this, SLOT(materialIndiceChanged(double)));
    hLayout->addWidget(new QLabel("Indice: "));
    hLayout->addWidget(indiceSpin);
    objLayout->addLayout(hLayout);

    hLayout = new QHBoxLayout();
    glossSpin = new QDoubleSpinBox();
    glossSpin->setRange(0.0, 10.0);
    glossSpin->setSingleStep(0.1);
    connect(glossSpin, SIGNAL(valueChanged(double)), this, SLOT(materialGlossChanged(double)));
    hLayout->addWidget(new QLabel("Gloss: "));
    hLayout->addWidget(glossSpin);
    objLayout->addLayout(hLayout);

    filterGlossCB = new QCheckBox("Filter gloss");
    connect(filterGlossCB, SIGNAL(toggled(bool)), this, SLOT(materialGlossFilteringChanged(bool)));
    objLayout->addWidget(filterGlossCB);

	colorButton = new QPushButton ("Change color");
    objLayout->addWidget (colorButton);
    connect (colorButton, SIGNAL (clicked ()), this, SLOT (materialColorChanged()));

    layout->addWidget(objGroupBox);

    // ---------------------------------
    // Scene
    // ---------------------------------
    QGroupBox * sceneGroupBox = new QGroupBox ("Scene");
    QVBoxLayout* sceneLayout = new QVBoxLayout (sceneGroupBox);

    QPushButton * bgColorButton  = new QPushButton ("Background color");
    connect (bgColorButton, SIGNAL (clicked()) , this, SLOT (setBGColor()));
    sceneLayout->addWidget (bgColorButton);

    QCheckBox * useLights  = new QCheckBox ("Raytrace lights");
    useLights->setChecked(true);
    connect (useLights, SIGNAL (toggled(bool)) , this, SLOT (setRaytraceLights(bool)));
    sceneLayout->addWidget (useLights);

    QCheckBox * shadowsEnabled  = new QCheckBox ("Use shadows");
    connect (shadowsEnabled, SIGNAL (toggled(bool)) , this, SLOT (enableShadows(bool)));
    sceneLayout->addWidget (shadowsEnabled);

	QPushButton * loadSceneButton  = new QPushButton ("Load scene");
	connect (loadSceneButton, SIGNAL (clicked()) , this, SLOT (loadScene()));
	sceneLayout->addWidget (loadSceneButton);

    layout->addWidget(sceneGroupBox);

    layout->addStretch (0);

	QScrollArea* controlArea = new QScrollArea();
	controlArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	controlArea->setWidget(controlWidget);
	QDockWidget* controlDockWidget = new QDockWidget (this);
	controlDockWidget->setWidget(controlArea);
    controlDockWidget->adjustSize ();
    controlDockWidget->setFeatures (QDockWidget::AllDockWidgetFeatures);
    return controlDockWidget;
}

void Window::resetDofObjButtons()
{
    dofButton->setChecked(false);
    objButton->setChecked(false);
}

void Window::selectObject()
{
    viewer->setClickMode(GLViewer::Select);
}

void Window::setSelectedObject(Object* obj)
{
    if(selectedObj)
        selectedObj->setSelected(false);
    selectedObj = 0;
    mat = 0;

    if(obj)
    {
        selectedObj = obj;
        selectedObj->setSelected(true);
        mat = &obj->getMaterial();
		viewer->updateGL();

        // Update gui
        diffuseSpin->setEnabled(true);
        specularSpin->setEnabled(true);
        transparentSpin->setEnabled(true);
        indiceSpin->setEnabled(true);
        glossSpin->setEnabled(true);
        shininessSpin->setEnabled(true);
        pureMirrorCB->setEnabled(true);
        filterGlossCB->setEnabled(true);
        colorButton->setEnabled(true);

        diffuseSpin->setValue(mat->getDiffuse());
        specularSpin->setValue(mat->getSpecular());
        transparentSpin->setValue(mat->getTransparency());
        indiceSpin->setValue(mat->getIndice());
        glossSpin->setValue(mat->getGloss());
        shininessSpin->setValue(mat->getShininess());
        pureMirrorCB->setChecked(mat->getIsMirror());
        filterGlossCB->setChecked(mat->isGlossFiltered());
    }
    else
    {
        diffuseSpin->setEnabled(false);
        specularSpin->setEnabled(false);
        transparentSpin->setEnabled(false);
        indiceSpin->setEnabled(false);
        glossSpin->setEnabled(false);
        shininessSpin->setEnabled(false);
        pureMirrorCB->setEnabled(false);
        filterGlossCB->setEnabled(false);
        colorButton->setEnabled(false);
    }
}

void Window::materialDiffuseChanged(double v)
{
    if(mat) mat->setDiffuse(v);
	viewer->updateGL();
}

void Window::materialSpecularChanged(double v)
{
	if(mat) mat->setSpecular(v);
	viewer->updateGL();
}

void Window::materialTransparencyChanged(double v)
{
    if(mat) mat->setTransparency(v);
	viewer->updateGL();
}

void Window::materialIndiceChanged(double v)
{
    if(mat) mat->setIndice(v);
	viewer->updateGL();
}

void Window::materialGlossChanged(double v)
{
    if(mat) mat->setGloss(v);
	viewer->updateGL();
}

void Window::materialShininessChanged(int v)
{
    if(mat) mat->setShininess(v);
	viewer->updateGL();
}

void Window::materialPureMirrorChanged(bool v)
{
    if(mat) mat->setIsMirror(v);
	viewer->updateGL();
}

void Window::materialGlossFilteringChanged(bool v)
{
    if(mat) mat->setGlossFiltered(v);
	viewer->updateGL();
}

void Window::materialColorChanged()
{
    if(mat)
    {
        QColor color = QColorDialog::getColor(mat->getColor().toQColor(), 0, "Choose material color");
        mat->setColor(Colorf(color));
		viewer->updateGL();
    }
}

void Window::setRaytraceLights(bool b)
{
    RayTracer::getInstance()->setRaytraceLights(b);
}

void Window::enableShadows(bool b)
{
    Scene::getInstance()->enableShadows(b);
}

void Window::loadScene()
{
	// Epic file filter
	QString filename = QFileDialog::getOpenFileName(this,"Choose a scene", "",
													"All models (*.dae *.blend *.3ds *.ase *.obj *.ifc *.xgl *.zgl *.ply *.lwo *.lws *.lxo *.stl *.x *.ac *.ms3d *.bvh *.xml *.irrmesh *.mdl *.md2 *.md3 *.pk3 *.b3d *.q3d *.q3s *.nff *.nff *.off *.raw *.ter *.mdl *.hmp *.ndo);;"
													"Collada (*.dae);;"
													"Blender 3D (*.blend);;"
													"3ds Max 3DS (*.3ds);;"
													"3ds Max ASE (*.ase);;"
													"Wavefront Object (*.obj);;"
													"Industry Foundation Classes (IFC/Step) (*.ifc);;"
													"XGL (*.xgl *.zgl);;"
													"Stanford Polygon Library (*.ply);;"
													"LightWave (*.lwo);;"
													"LightWave Scene (*.lws);;"
													"Modo (*.lxo);;"
													"Stereolithography (*.stl);;"
													"DirectX X (*.x);;"
													"AC3D (*.ac);;"
													"Milkshape 3D (*.ms3d);;"
													"Biovision BVH (*.bvh);;"
													"Ogre XML (*.xml);;"
													"Irrlicht Mesh (*.irrmesh);;"
													"Quake I (*.mdl);;"
													"Quake II (*.md2);;"
													"Quake III Mesh (*.md3);;"
													"Quake III Map/BSP (*.pk3);;"
													"BlitzBasic 3D (*.b3d);;"
													"Quick3D (*.q3d *.q3s);;"
													"Neutral File Format (*.nff);;"
													"Sense8 WorldToolKit (*.nff);;"
													"Object File Format (*.off);;"
													"PovRAY Raw (*.raw);;"
													"Terragen Terrain (*.ter);;"
													"3D GameStudio (3DGS) (*.mdl);;"
													"3D GameStudio (3DGS) Terrain (*.hmp);;"
													"Izware Nendo (*.ndo)"
													);

	if(filename != "")
	{
		QMessageBox::information(this,"Loading scene", "Warning: some formats are not fully supported. Lights might be missing.");
		Scene::getInstance()->loadScene(filename.toStdString());
	}
}
