/***************************************************************************
 *   Copyright (c) 2013 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#include "PreCompiled.h"

#ifndef _PreComp_
#endif

#include <QGLWidget>
#include "GLPainter.h"
#include "View3DInventorViewer.h"
#include <Base/Console.h>

using namespace Gui;

TYPESYSTEM_SOURCE_ABSTRACT(Gui::GLGraphicsItem, Base::BaseClass);

GLPainter::GLPainter() : viewer(0), logicOp(false), lineStipple(false)
{
}

GLPainter::~GLPainter()
{
    end();
}

bool GLPainter::begin(QPaintDevice * device)
{
    if (viewer)
        return false;

    viewer = dynamic_cast<QGLWidget*>(device);
    if (!viewer)
        return false;

    // Make current context
    QSize view = viewer->size();
    this->width = view.width();
    this->height = view.height();

    viewer->makeCurrent();

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, this->width, 0, this->height, -1, 1);

    // Store GL state
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glGetFloatv(GL_DEPTH_RANGE, this->depthrange);
    glGetDoublev(GL_PROJECTION_MATRIX, this->projectionmatrix);

    glDepthFunc(GL_ALWAYS);
    glDepthMask(GL_TRUE);
    glDepthRange(0,0);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);
    glDisable(GL_BLEND);

    glLineWidth(1.0f);
    glColor4f(1.0, 1.0, 1.0, 0.0);
    glViewport(0, 0, this->width, this->height);
    glDrawBuffer(GL_FRONT);

    return true;
}

bool GLPainter::end()
{
    if (!viewer)
        return false;

    glFlush();

    if (this->logicOp) {
        this->logicOp = false;
        glDisable(GL_COLOR_LOGIC_OP);
    }

    if (this->lineStipple) {
        this->lineStipple = false;
        glDisable(GL_LINE_STIPPLE);
    }

    // Reset original state
    glDepthRange(this->depthrange[0], this->depthrange[1]);
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixd(this->projectionmatrix);

    glPopAttrib();
    glPopMatrix();

    viewer = 0;
    return true;
}

bool GLPainter::isActive() const
{
    return viewer != 0;
}

void GLPainter::setLineWidth(float w)
{
    glLineWidth(w);
}

void GLPainter::setPointSize(float s)
{
    glPointSize(s);
}

void GLPainter::setColor(float r, float g, float b, float a)
{
    glColor4f(r, g, b, a);
}

void GLPainter::setLogicOp(GLenum mode)
{
    glEnable(GL_COLOR_LOGIC_OP);
    glLogicOp(mode);
    this->logicOp = true;
}

void GLPainter::resetLogicOp()
{
    glDisable(GL_COLOR_LOGIC_OP);
    this->logicOp = false;
}

void GLPainter::setDrawBuffer(GLenum mode)
{
    glDrawBuffer(mode);
}

void GLPainter::setLineStipple(GLint factor, GLushort pattern)
{
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(factor, pattern);
    this->lineStipple = true;
}

void GLPainter::resetLineStipple()
{
    glDisable(GL_LINE_STIPPLE);
    this->lineStipple = false;
}

// Draw routines
void GLPainter::drawRect(int x1, int y1, int x2, int y2)
{
    if (!viewer)
        return;

    glBegin(GL_LINE_LOOP);
        glVertex3i(x1, this->height-y1, 0);
        glVertex3i(x2, this->height-y1, 0);
        glVertex3i(x2, this->height-y2, 0);
        glVertex3i(x1, this->height-y2, 0);
    glEnd();
}

void GLPainter::drawLine(int x1, int y1, int x2, int y2)
{
    if (!viewer)
        return;

    glBegin(GL_LINES);
        glVertex3i(x1, this->height-y1, 0);
        glVertex3i(x2, this->height-y2, 0);
    glEnd();
}

void GLPainter::drawPoint(int x, int y)
{
    if (!viewer)
        return;

    glBegin(GL_POINTS);
        glVertex3i(x, this->height-y, 0);
    glEnd();
}

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

Rubberband::Rubberband(View3DInventorViewer* v) : viewer(v)
{
    x_old = y_old = x_new = y_new = 0;
    working = false;
    stipple = true;
}

Rubberband::Rubberband()
{
    x_old = y_old = x_new = y_new = 0;
    working = false;
    stipple = true;
}

Rubberband::~Rubberband()
{
}

void Rubberband::setWorking(bool on)
{
    working = on;
}

void Rubberband::setViewer(View3DInventorViewer* v)
{
    viewer = v;
}

void Rubberband::setCoords(int x1, int y1, int x2, int y2)
{
    x_old = x1;
    y_old = y1;
    x_new = x2;
    y_new = y2;
}

void Rubberband::setLineStipple(bool on)
{
    stipple = on;
}

void Rubberband::setColor(float r, float g, float b, float a)
{
    rgb_a = a;
    rgb_b = b;
    rgb_g = g;
    rgb_r = r;
}

void Rubberband::paintGL()
{
    if (!working)
        return;

    const SbViewportRegion vp = viewer->getSoRenderManager()->getViewportRegion();
    SbVec2s size = vp.getViewportSizePixels();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, size[0], size[1], 0, 0, 100);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glLineWidth(4.0);
    glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
    glRecti(x_old, y_old, x_new, y_new);

    glLineWidth(4.0);
    glColor4f(rgb_r, rgb_g, rgb_b, rgb_a);
    if (stipple) {
        glLineStipple(3, 0xAAAA);
        glEnable(GL_LINE_STIPPLE);
    }
    glBegin(GL_LINE_LOOP);
    glVertex2i(x_old, y_old);
    glVertex2i(x_old, y_new);
    glVertex2i(x_new, y_new);
    glVertex2i(x_new, y_old);
    glEnd();

    glLineWidth(1.0);

    if (stipple)
        glDisable(GL_LINE_STIPPLE);

    glDisable(GL_BLEND);
}

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

Polyline::Polyline(View3DInventorViewer* v) : viewer(v)
{
    x_new = y_new = 0;
    working = false;
    closed = true;
    stippled = false;
    line = 2.0;

    rgb_r = 1.0f;
    rgb_g = 1.0f;
    rgb_b = 1.0f;
    rgb_a = 1.0f;
}

Polyline::Polyline()
{
    x_new = y_new = 0;
    working = false;
    closed = true;
    stippled = false;
    line = 2.0;

    rgb_r = 1.0f;
    rgb_g = 1.0f;
    rgb_b = 1.0f;
    rgb_a = 1.0f;
}

Polyline::~Polyline()
{
}

void Polyline::setWorking(bool on)
{
    working = on;
}

bool Polyline::isWorking() const
{
    return working;
}

void Polyline::setViewer(View3DInventorViewer* v)
{
    viewer = v;
}

void Polyline::setCoords(int x, int y)
{
    x_new = x;
    y_new = y;
}

void Polyline::setColor(int r, int g, int b, int a)
{
    rgb_r = r;
    rgb_g = g;
    rgb_b = b;
    rgb_a = a;
}

void Polyline::setClosed(bool c)
{
    closed = c;
}

void Polyline::setCloseStippled(bool c)
{
    stippled = c;
}

void Polyline::setLineWidth(float l)
{
    line = l; 
}

void Polyline::addNode(const QPoint& p)
{
    _cNodeVector.push_back(p);
}

void Polyline::popNode()
{
    if (!_cNodeVector.empty())
        _cNodeVector.pop_back();
}

void Polyline::clear()
{
    _cNodeVector.clear();
}

void Polyline::paintGL()
{
    if (!working)
        return;

    if (_cNodeVector.empty())
        return;

    const SbViewportRegion vp = viewer->getSoRenderManager()->getViewportRegion();
    SbVec2s size = vp.getViewportSizePixels();

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, size[0], size[1], 0, 0, 100);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glLineWidth(line);
    glColor4f(rgb_r, rgb_g, rgb_b, rgb_a);

    if (closed && !stippled) {
        glBegin(GL_LINE_LOOP);

        for (std::vector<QPoint>::iterator it = _cNodeVector.begin(); it != _cNodeVector.end(); ++it) {
            glVertex2i(it->x(), it->y());
        }

        glEnd();
    }
    else {
        glBegin(GL_LINES);

        QPoint start = _cNodeVector.front();
        for (std::vector<QPoint>::iterator it = _cNodeVector.begin(); it != _cNodeVector.end(); ++it) {
            glVertex2i(start.x(), start.y());
            start = *it;
            glVertex2i(it->x(), it->y());
        }

        glEnd();

        if (closed && stippled) {
            glEnable(GL_LINE_STIPPLE);
            glLineStipple(2, 0x3F3F);
            glBegin(GL_LINES);
                glVertex2i(_cNodeVector.back().x(), _cNodeVector.back().y());
                glVertex2i(_cNodeVector.front().x(), _cNodeVector.front().y());
            glEnd();
            glDisable(GL_LINE_STIPPLE);
        }
    }

    glDisable(GL_BLEND);
}
