/***************************************************************************
 *   Copyright (c) 2007 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_
# include <sstream>
#endif

#include <Inventor/SbBox.h>
#include <Inventor/SoPrimitiveVertex.h>
#include <Inventor/actions/SoGLRenderAction.h>
#include <Inventor/bundles/SoMaterialBundle.h>
#include <Inventor/elements/SoLightModelElement.h>
#include <Inventor/elements/SoLazyElement.h>
#include <Inventor/elements/SoMaterialBindingElement.h>
#include <Inventor/elements/SoModelMatrixElement.h>
#include <Inventor/misc/SoState.h>

#include <string.h>
#include <iostream>

#include "SoFCBoundingBox.h"

using namespace Gui;

SO_NODE_SOURCE(SoFCBoundingBox);

// vertices used to create a box
static const int32_t bBoxVerts[8][3] =
{
    {0, 0, 0},
    {1, 0, 0},
    {1, 1, 0},
    {0, 1, 0},
    {0, 0, 1},
    {1, 0, 1},
    {1, 1, 1},
    {0, 1, 1}
};

// indexes used to create the edges
static const int32_t bBoxEdges[36] =
{
    0,1,-1, 1,2,-1, 2,3,-1, 3,0,-1,
    4,5,-1, 5,6,-1, 6,7,-1, 7,4,-1,
    0,4,-1, 1,5,-1, 2,6,-1, 3,7,-1
};

void SoFCBoundingBox::initClass ()
{
    SO_NODE_INIT_CLASS(SoFCBoundingBox, SoShape, "Shape");
}

SoFCBoundingBox::SoFCBoundingBox ()
{
    SO_NODE_CONSTRUCTOR(SoFCBoundingBox);

    SO_NODE_ADD_FIELD(minBounds, (-1.0, -1.0, -1.0));
    SO_NODE_ADD_FIELD(maxBounds, ( 1.0,  1.0,  1.0));
    SO_NODE_ADD_FIELD(coordsOn, (true));
    SO_NODE_ADD_FIELD(dimensionsOn, (true));

    root = new SoSeparator();
    SoSeparator *bboxSep = new SoSeparator();

    bboxCoords = new SoCoordinate3();
    bboxCoords->point.setNum(8);
    bboxSep->addChild(bboxCoords);
    root->addChild(bboxSep);

    // the lines of the box
    bboxLines  = new SoIndexedLineSet();
    bboxLines->coordIndex.setNum(36);
    bboxLines->coordIndex.setValues(0, 36, bBoxEdges);
    bboxSep->addChild(bboxLines);
  

    // create the text nodes, including a transform for each vertice offset
    textSep = new SoSeparator();
    for (int i = 0; i < 8; i++) {
        SoSeparator *temp = new SoSeparator();
        SoTransform *trans = new SoTransform();
        temp->addChild(trans);
        SoText2* text = new SoText2();
        text->justification.setValue(SoText2::CENTER);
        temp->addChild(text);
        textSep->addChild(temp);
    }

    // create the text nodes, including a transform for each dimension
    dimSep = new SoSeparator();
    for (int i = 0; i < 3; i++) {
        SoSeparator *temp = new SoSeparator();
        SoTransform *trans = new SoTransform();
        temp->addChild(trans);
        SoText2* text = new SoText2();
        text->justification.setValue(SoText2::CENTER);
        temp->addChild(text);
        dimSep->addChild(temp);
    }

    root->addChild(textSep);
    root->addChild(dimSep);
    root->ref();
}

SoFCBoundingBox::~SoFCBoundingBox ()
{
    root->unref();
}

void SoFCBoundingBox::GLRender (SoGLRenderAction *action)
{
    SbVec3f corner[2], ctr, *vptr;
    SbBool coord, dimension;

    // grab the current state
    //SoState *state = action->getState();

    if (!shouldGLRender(action))
        return;

    // get the latest values from the fields
    corner[0] = minBounds.getValue();
    corner[1] = maxBounds.getValue();
    coord     = coordsOn.getValue();
    dimension = dimensionsOn.getValue();

    // set the coordinates for the LineSet to point to
    vptr = bboxCoords->point.startEditing();
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 3; j++) {
            vptr[i][j] = corner[bBoxVerts[i][j]][j];
        }
    }

    // if coord is true then set the text nodes
    if (coord) {
        ctr = (corner[1] - corner[0]) / 2.0f;
        for (int i = 0; i < 8; i++) {
            // create the string for the text
            std::stringstream str;
            str.precision(2);
            str.setf(std::ios::fixed | std::ios::showpoint);
            str << "(" << vptr[i][0] << "," << vptr[i][1] << "," << vptr[i][2] << ")";

            SoSeparator *sep   = (SoSeparator *)textSep->getChild(i);
            SoTransform *trans = (SoTransform *)sep->getChild(0);

            trans->translation.setValue(vptr[i].getValue());
            SoText2* t = (SoText2 *)sep->getChild(1);
            t->string.setValue(str.str().c_str());
        }

        textSep->ref();
        if (root->findChild(textSep) < 0)
            root->addChild(textSep);
    } else {
        if (root->findChild(textSep) >= 0)
            root->removeChild(textSep);
    }

    // if dimension is true then set the text nodes
    if (dimension) {
        ctr = (corner[1] - corner[0]) / 2.0f;
        for (int i = 0; i < 3; i++) {
            // create the string for the text
            std::stringstream str;
            str.precision(2);
            str.setf(std::ios::fixed | std::ios::showpoint);
            str << (2.0f * ctr[i]);

            SoSeparator *sep   = (SoSeparator *)dimSep->getChild(i);
            SoTransform *trans = (SoTransform *)sep->getChild(0);

            SbVec3f point = corner[0];
            point[i] += ctr[i];
            trans->translation.setValue(point.getValue());
            SoText2* t = (SoText2 *)sep->getChild(1);
            t->string.setValue(str.str().c_str());
        }

        dimSep->ref();
        if (root->findChild(dimSep) < 0)
            root->addChild(dimSep);
    } else {
        if (root->findChild(dimSep) >= 0)
            root->removeChild(dimSep);
    }

    bboxCoords->point.finishEditing();

    // Avoid shading
    SoState * state = action->getState();
    state->push();
    SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
    root->GLRender(action);
    state->pop();
}

void SoFCBoundingBox::generatePrimitives (SoAction * /*action*/)
{
}

void SoFCBoundingBox::computeBBox (SoAction * /*action*/, SbBox3f &box, SbVec3f &center)
{
    center = (minBounds.getValue() + maxBounds.getValue()) / 2.0f;
    box.setBounds(minBounds.getValue(), maxBounds.getValue());
}

void SoFCBoundingBox::finish()
{
  atexit_cleanup();
}

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

SO_NODE_SOURCE(SoSkipBoundingGroup);

/*!
  Constructor.
*/
SoSkipBoundingGroup::SoSkipBoundingGroup()
{
    SO_NODE_CONSTRUCTOR(SoSkipBoundingGroup);

    SO_NODE_ADD_FIELD(mode,          (INCLUDE_BBOX));

    SO_NODE_DEFINE_ENUM_VALUE(Modes, INCLUDE_BBOX);
    SO_NODE_DEFINE_ENUM_VALUE(Modes, EXCLUDE_BBOX);
    SO_NODE_SET_SF_ENUM_TYPE (mode, Modes);
}

/*!
  Destructor.
*/
SoSkipBoundingGroup::~SoSkipBoundingGroup()
{
}

void
SoSkipBoundingGroup::initClass(void)
{
    SO_NODE_INIT_CLASS(SoSkipBoundingGroup,SoGroup,"Group");
}

void SoSkipBoundingGroup::finish()
{
    atexit_cleanup();
}

void SoSkipBoundingGroup::getBoundingBox(SoGetBoundingBoxAction *action)
{
    if (mode.getValue() == INCLUDE_BBOX)
        inherited::getBoundingBox(action);
}

