/*
 * MeshBasedNode.cpp
 *
 * Copyright (C) 1999 Stephen F. White, 2004 J. "MUFTI" Scheurich
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <fcntl.h>
# include <sys/wait.h>
#endif

#include "swt.h"
#include "resource.h"
#include "stdafx.h"
#include "MeshBasedNode.h"
#include "Scene.h"
#include "FieldValue.h"
#include "SFInt32.h"
#include "Mesh.h"
#include "Face.h"
#include "MFFloat.h"
#include "MFInt32.h"
#include "MFVec2f.h"
#include "MFVec3f.h"
#include "SFNode.h"
#include "SFBool.h"
#include "SFVec3f.h"
#include "Vec2f.h"
#include "Vec3f.h"
#include "RenderState.h"
#include "DuneApp.h"
#include "NodeShape.h"
#include "NodeCoordinate.h"
#include "NodeNormal.h"
#include "NodeTextureCoordinate.h"
#include "NodeColor.h"
#include "NodeColorRGBA.h"
#include "NodeIndexedFaceSet.h"
#include "NodeIndexedTriangleSet.h"
#include "NodeTriangleSet.h"
#include "NodeElevationGrid.h"
#include "NodeGeoElevationGrid.h"
#include "NodeCone.h"
#include "LdrawDefines.h"

MeshBasedNode::MeshBasedNode(Scene *scene, Proto *proto)
  : GeometryNode(scene, proto)
{
    _isMesh = true;
    _mesh = NULL;
    _meshDirty = true;
}

MeshBasedNode::~MeshBasedNode()
{
    delete _mesh;
}


void
MeshBasedNode::meshDraw()
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }

    if (!_mesh) return;

    _mesh->sort();
    _mesh->draw();
}

void
MeshBasedNode::drawNormals()
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }

    if (!_mesh) return;

    _mesh->drawNormals();
}

Node * 
MeshBasedNode::toIndexedFaceSet(int meshFlags, bool cleanVertices)
{
    bool wantNormal = (meshFlags & MESH_WANT_NORMAL);
    if (meshDirty()) {
        createMesh(cleanVertices);
        _meshDirty = false;
    }

    if (!_mesh) return NULL;

    NodeCoordinate *ncoord = (NodeCoordinate *)_scene->createNode("Coordinate");
    MFVec3f *points = new MFVec3f();
    MFVec3f *vertices = _mesh->getVertices();
    for (int i = 0; i < vertices->getSFSize(); i++)
        points->setSFValue(i, vertices->getValue(i)); 
    ncoord->point(points);
    NodeIndexedFaceSet *node = (NodeIndexedFaceSet *)
                               _scene->createNode("IndexedFaceSet");
    node->coord(new SFNode(ncoord));
    if (wantNormal) {
        NodeNormal *nnormal = NULL;
        if (getSmoothNormals()) {
            nnormal = (NodeNormal *)_scene->createNode("Normal");
            nnormal->vector(getSmoothNormals());
        }
        if (nnormal) {
            node->normal(new SFNode(nnormal));
            MFInt32 *ni = getSmoothNormalIndex();
            if (ni != NULL)
                node->normalIndex(ni);
        } 
    } 
    node->normalPerVertex(new SFBool(_mesh->normalPerVertex()));
    node->creaseAngle(new SFFloat(_mesh->creaseAngle())); 
    node->coordIndex(new MFInt32(_mesh->getCoordIndex()));
    node->solid(new SFBool(_mesh->solid()));
    node->ccw(new SFBool(_mesh->ccw()));
    node->convex(new SFBool(_mesh->convex()));
    NodeTextureCoordinate *ntexCoord = NULL;
    if (_mesh->getTexCoords()) {
        ntexCoord = (NodeTextureCoordinate *)
                    _scene->createNode("TextureCoordinate");
        ntexCoord->point(_mesh->getTexCoords());
    }
    if (ntexCoord) {
        node->texCoord(new SFNode(ntexCoord));
        node->texCoordIndex(_mesh->getTexCoordIndex());
    }

    node->ref();
    return node;
}

#define createNormalFromMesh(node) \
{ \
    NodeNormal *nnormal = (NodeNormal *)_scene->createNode("Normal"); \
    node->setNormalFromMesh(nnormal); \
    node->normal(new SFNode(nnormal)); \
}

#define createTexCoordFromMesh(node) \
{ \
    NodeTextureCoordinate *ntexCoord = (NodeTextureCoordinate *) \
         _scene->createNode("TextureCoordinate"); \
    node->setTexCoordFromMesh(ntexCoord); \
    node->texCoord(new SFNode(ntexCoord)); \
}

Node * 
MeshBasedNode::toTriangleSet(int meshFlags)
{
    bool targetHasCcw = (meshFlags & MESH_TARGET_HAS_CCW);
    bool wantNormal = (meshFlags & MESH_WANT_NORMAL);
    bool wantTexCoord = (meshFlags & MESH_WANT_TEX_COORD);

    if (meshDirty()) {
        float creaseAngle = _mesh ? _mesh->creaseAngle() : 0;
        createMesh(true);
        if (_mesh)
            _mesh->setCreaseAngle(creaseAngle);
        _meshDirty = false;
    }

    if (!_mesh) return NULL;

    Mesh *mesh = triangulateMesh();

    NodeCoordinate *ncoord = (NodeCoordinate *)_scene->createNode("Coordinate");
    MFVec3f *vertices = mesh->getVertices();
    MFVec3f *points = new MFVec3f();

    MFInt32 *coordIndices = mesh->getCoordIndex();

    MFVec3f *normals = mesh->getNormals();
    bool hasNormals = normals && normals->getValues();
    MFVec3f *vectors = new MFVec3f();

    bool hasColors = mesh->getColors() && mesh->getColors()->getValues();
    bool hasColorsRGBA = hasColors && mesh->hasColorRGBA();
    hasColors = hasColors && !mesh->hasColorRGBA();
    MFFloat *mfColors = mesh->getColors();
    MFColor *colors = hasColors ? new MFColor() : NULL;
    MFColorRGBA *colorsRGBA = hasColorsRGBA ? new MFColorRGBA() : NULL;

    MFVec2f *texCoords = mesh->getTexCoords();
    bool hasTexCoords = texCoords && texCoords->getValues();
    MFVec2f *coords = new MFVec2f();

    bool solid = mesh->solid();
    bool ccw = mesh->ccw();
    bool drawFront = !solid || !ccw;
    bool drawBack = !solid || ccw;
    if (targetHasCcw) {
        drawFront = true;
        drawBack = false;
    }
    int numVertices = 0;
    if (drawFront)
        for (int i = 0; i < coordIndices->getSize(); i++) {
            int index = coordIndices->getValue(i);
            if (index >= 0) {
                points->setSFValue(numVertices, vertices->getValue(index)); 
                if (hasNormals)
                    vectors->setSFValue(numVertices, 
                                        normals->getValue(index)); 
                if (hasColors)
                    colors->setSFValue(numVertices, 
                                       mfColors->getValues() + index * 3); 
                if (hasColorsRGBA)
                    colorsRGBA->setSFValue(numVertices, 
                                           mfColors->getValues() + index * 4); 
                if (hasTexCoords)
                    coords->setSFValue(numVertices, 
                                       texCoords->getValue(index)); 

                numVertices++;
            }
        }
    if (drawBack)
        for (int i = coordIndices->getSize() - 1; i > -1; i--) {
            int index = coordIndices->getValue(i);
            if (index >= 0) {
                points->setSFValue(numVertices, vertices->getValue(index)); 
                if (hasNormals)
                    vectors->setSFValue(numVertices, 
                                        normals->getValue(index)); 
                if (hasColors)
                    colors->setSFValue(numVertices, 
                                       mfColors->getValues() + index * 3); 
                if (hasColorsRGBA)
                    colorsRGBA->setSFValue(numVertices, 
                                           mfColors->getValues() + index * 4); 
                if (hasTexCoords)
                    coords->setSFValue(numVertices, 
                                       texCoords->getValue(index)); 

                numVertices++;
            }
        }
    ncoord->point(points);
    NodeTriangleSet *node = (NodeTriangleSet *)
                            _scene->createNode("TriangleSet");
    node->coord(new SFNode(ncoord));
    node->solid(new SFBool(mesh->solid()));
    node->ccw(new SFBool(mesh->ccw()));

    if (hasNormals || wantNormal) {
        NodeNormal *nnormal = (NodeNormal *)_scene->createNode("Normal");
        if (hasNormals) {
            nnormal->vector(new MFVec3f(vectors));
        }
        node->normal(new SFNode(nnormal));
    }

    if (hasColors) {
        NodeColor *ncolor = (NodeColor *) _scene->createNode("Color");
        ncolor->color(new MFColor(colors));
        node->color(new SFNode(ncolor));
    }
    if (hasColorsRGBA) {
        NodeColorRGBA *ncolor = (NodeColorRGBA *) 
                                _scene->createNode("ColorRGBA");
        ncolor->color(new MFColorRGBA(colorsRGBA));
        node->color(new SFNode(ncolor));
    }

    if (hasTexCoords) {
        NodeTextureCoordinate *ntexCoord = (NodeTextureCoordinate *)
                                           _scene->createNode("TextureCoordinate");
        ntexCoord->point(new MFVec2f(coords));
        node->texCoord(new SFNode(ntexCoord));
    } else if (wantTexCoord)
        createTexCoordFromMesh(node);
    
    delete mesh;

    return node;
}

Node * 
MeshBasedNode::toIndexedTriangleSet(int meshFlags)
{
    bool targetHasCcw = (meshFlags & MESH_TARGET_HAS_CCW);
    bool wantNormal = (meshFlags & MESH_WANT_NORMAL);
    bool wantTexCoord = (meshFlags & MESH_WANT_TEX_COORD);

    if (meshDirty()) {
        float creaseAngle = _mesh ? _mesh->creaseAngle() : 0;
        createMesh(true);
        if (_mesh)
            _mesh->setCreaseAngle(creaseAngle);
        _meshDirty = false;
    }

    if (!_mesh) return NULL;

    bool solid = _mesh->solid();
    bool ccw = _mesh->ccw();

    NodeTriangleSet *triangles = (NodeTriangleSet *)toTriangleSet(meshFlags);
    if (triangles == NULL)
        return NULL;
    MFVec3f *mfVertices = new MFVec3f();
    MFInt32 *mfCoordIndex = new MFInt32();
    if (optimizeOnConvertionToIndexedTriangleSet())
        triangles->optimizeMesh(mfVertices, mfCoordIndex);
    int i;
    bool drawFront = !solid || !ccw;
    bool drawBack = !solid || ccw;
    if (targetHasCcw) {
        drawFront = true;
        drawBack = false;
    }
    int indexCount = 0;
    int size = mfCoordIndex->getSize();
    if (drawFront)
        for (i = 0; i < size; i++) {
            if (mfCoordIndex->getValue(i) > -1)
                mfCoordIndex->setSFValue(indexCount++, 
                                         mfCoordIndex->getValue(i));
        }
    if (drawBack) {
        for (i = size - 1; i > -1; i--) {
            if (mfCoordIndex->getValue(i) > -1)
                mfCoordIndex->setSFValue(indexCount++, 
                                         mfCoordIndex->getValue(i));
        }
    }
    int indexSize = mfCoordIndex->getSize();
    for (i = indexCount; i < indexSize; i++)
        mfCoordIndex->removeSFValue(i);
    NodeIndexedTriangleSet *node = (NodeIndexedTriangleSet *)
                                   _scene->createNode("IndexedTriangleSet");
    NodeCoordinate *ncoord = (NodeCoordinate *)
                             _scene->createNode("Coordinate");
    if (ncoord && mfVertices && mfCoordIndex) {
        ncoord->point(new MFVec3f(mfVertices));
        node->coord(new SFNode(ncoord));
        node->index(new MFInt32(mfCoordIndex));
        node->ccw(new SFBool(triangles->ccw()->getValue()));
        node->solid(new SFBool(triangles->solid()->getValue()));
    }

    if (wantNormal)
        createNormalFromMesh(node);

    if (wantTexCoord)
        createTexCoordFromMesh(node);

    return node;
}

Node * 
MeshBasedNode::alreadyTriangulatedToIndexedTriangleSet(bool hasCcw)
{
    if (meshDirty()) {
        float creaseAngle = _mesh ? _mesh->creaseAngle() : 0;
        createMesh(true);
        if (_mesh)
            _mesh->setCreaseAngle(creaseAngle);
        _meshDirty = false;
    }

    if (!_mesh) return NULL;

    // already triangulated

    MFInt32 *coordIndices = _mesh->getCoordIndex();
    MFInt32 *indices = new MFInt32();
    bool solid = _mesh->solid();
    bool ccw = _mesh->ccw();
    bool drawFront = !solid || !ccw;
    bool drawBack = !solid || ccw;
    if (hasCcw) {
        drawFront = true;
        drawBack = false;
    }
    int numIndices = 0;
    if (drawFront)
        for (int i = 0; i < coordIndices->getSize(); i++) {
            int index = coordIndices->getValue(i);
            if (index >= 0)
                indices->setSFValue(numIndices++, index); 
        }
    if (drawBack)
        for (int i = coordIndices->getSize() - 1; i > -1; i++) {
            int index = coordIndices->getValue(i);
            if (index >= 0)
                indices->setSFValue(numIndices++, index); 
        }

    NodeCoordinate *ncoord = (NodeCoordinate *)_scene->createNode("Coordinate");
    ncoord->point(_mesh->getVertices());
    NodeIndexedTriangleSet *node = (NodeIndexedTriangleSet *)
                               _scene->createNode("IndexedTriangleSet");
    node->coord(new SFNode(ncoord));
    NodeNormal *nnormal = NULL;
    if (_mesh->getNormals()) {
        nnormal = (NodeNormal *)_scene->createNode("Normal");
        nnormal->vector(_mesh->getNormals());
    }
    if (nnormal)
        node->normal(new SFNode(nnormal));
    node->normalPerVertex(new SFBool(_mesh->normalPerVertex()));
    node->index(new MFInt32(indices));
    node->solid(new SFBool(_mesh->solid()));
    node->ccw(new SFBool(_mesh->ccw()));
    NodeTextureCoordinate *ntexCoord = NULL;
    if (_mesh->getTexCoords()) {
        ntexCoord = (NodeTextureCoordinate *)
                    _scene->createNode("TextureCoordinate");
        ntexCoord->point(_mesh->getTexCoords());
    }
    if (ntexCoord)
        node->texCoord(new SFNode(ntexCoord));

    return node;
}

Vec3f
MeshBasedNode::getMinBoundingBox(void) 
{ 
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    Vec3f ret(0, 0, 0);
    if (_mesh && _mesh->getVertices())
        return _mesh->getVertices()->getMinBoundingBox();
    return ret;
}

Vec3f
MeshBasedNode::getMaxBoundingBox(void) 
{ 
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    Vec3f ret(0, 0, 0);
    if (_mesh && _mesh->getVertices())
        return _mesh->getVertices()->getMaxBoundingBox();
    return ret;
}

MFVec3f *
MeshBasedNode::getVertices(void)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
    return _mesh->getVertices();
}

MFVec2f *
MeshBasedNode::getTextureCoordinates()
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
    return _mesh->generateTextureCoordinates();
}

MFInt32 *
MeshBasedNode::getTexCoordIndex()
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
    return _mesh->getCoordIndex();
}

int
MeshBasedNode::optimizeNormals(int *coordIndex, Vec3f *vertices, 
                               Vec3f *normals, int length, bool ccw,
                               bool cleanDoubleVertices)
{
    int i;
    if (length < 2) 
        return length;
    int *newCoordIndex = new int[length];
    int newIndex = 1;
    newCoordIndex[0] = coordIndex[0];
    for (i = 1; i < length; i++)
        if (coordIndex[i-1] < 0)
            newCoordIndex[newIndex++] = coordIndex[i];
        else if (coordIndex[i] < 0)
            newCoordIndex[newIndex++] = coordIndex[i];
        else {
            if ((vertices[coordIndex[i-1]] - vertices[coordIndex[i]]).length() 
                > EPSILON)
                newCoordIndex[newIndex++] = coordIndex[i];
            else {
                // reaccount normal for remaining vertex i-1
                int index1 = i-2;
                int index2 = i;
                int index3 = i+1;
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    index1 = i-3;
                    index2 = i-2;
                    index3 = i;
                }
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    index1 = i;
                    index2 = i+1;
                    index3 = i+2;
                }
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    continue;
                }
                if (normals != NULL) {
                    Vec3f edge = (vertices[coordIndex[index2]] - 
                                  vertices[coordIndex[index1]]);
                    normals[coordIndex[i]] = edge.cross(
                                             vertices[coordIndex[index3]] - 
                                             vertices[coordIndex[index2]]);
                    if (!ccw) 
                        normals[coordIndex[i]] = -normals[coordIndex[i]];
                    normals[coordIndex[i]].normalize();
                    normals[coordIndex[i-1]] = normals[coordIndex[i]];
                }
            }
        }
      if (cleanDoubleVertices) {
          for (i = 0; i < newIndex; i++)
              coordIndex[i] = newCoordIndex[i];
          delete []  newCoordIndex;
      } else
          newIndex = length;
      return newIndex;
}

int
MeshBasedNode::optimizeNormals(int *coordIndex, MFVec3f *vertices, 
                               MFVec3f *normals, int length, bool ccw,
                               bool cleanDoubleVertices)
{
    int i;
    if (length < 2) 
        return length;
    int *newCoordIndex = new int[length];
    int newIndex = 1;
    newCoordIndex[0] = coordIndex[0];
    for (i = 1; i < length; i++)
        if (coordIndex[i-1] < 0)
            newCoordIndex[newIndex++] = coordIndex[i];
        else if (coordIndex[i] < 0)
            newCoordIndex[newIndex++] = coordIndex[i];
        else {
            if ((vertices->getVec(coordIndex[i-1]) - 
                 vertices->getVec(coordIndex[i])).length() > EPSILON)
                newCoordIndex[newIndex++] = coordIndex[i];
            else {
                // reaccount normal for remaining vertex i-1
                int index1 = i-2;
                int index2 = i;
                int index3 = i+1;
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    index1 = i-3;
                    index2 = i-2;
                    index3 = i;
                }
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    index1 = i;
                    index2 = i+1;
                    index3 = i+2;
                }
                if ((index1 < 0) ||
                    (index3 > length) || 
                    (coordIndex[index1] < 0) ||
                    (coordIndex[index2] < 0) ||   
                    (coordIndex[index3] < 0)) {    
                    continue;
                }
                if (normals != NULL) {
                    Vec3f edge1 = vertices->getVec(coordIndex[index2]) - 
                                  vertices->getVec(coordIndex[index1]);
                    Vec3f edge2 = vertices->getVec(coordIndex[index3]) - 
                                  vertices->getVec(coordIndex[index2]);
                    Vec3f normal = edge1.cross(edge2);
                    if (!ccw) 
                        normal = -normal;
                    normal.normalize();
                    normals->setVec(coordIndex[i-1], normal);
                    normals->setVec(coordIndex[i], normal);
                }
            }
        }
      if (cleanDoubleVertices) {
          for (i = 0; i < newIndex; i++)
              coordIndex[i] = newCoordIndex[i];
          delete []  newCoordIndex;
      } else
          newIndex = length;
      return newIndex;
}

int 
MeshBasedNode::countPolygons(void)
{
    int ret = 0;
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (!_mesh) return ret;
    MFInt32 *coordIndex = _mesh->getCoordIndex();
    if (coordIndex != NULL) {     
        int lastCoordIndex = -1;
        for (int i = 0; i < coordIndex->getSize(); i++) {
            if ((coordIndex->getValue(i) == -1) && (lastCoordIndex != -1))
                ret++;
            lastCoordIndex = coordIndex->getValue(i);
        }
        // need to count last polygon ?
        if (coordIndex->getSize() > 2)
            if (coordIndex->getValue(coordIndex->getSize() - 1) != -1)
                ret++;
    }
    return ret;
}

int 
MeshBasedNode::countPolygons2Sided(void)
{
    return isDoubleSided() ? 2 * MeshBasedNode::countPolygons() : 0;
}

int 
MeshBasedNode::countPolygons1Sided(void)
{
    return isDoubleSided() ? 0 : MeshBasedNode::countPolygons();
}

MFVec3f *
MeshBasedNode::getSmoothNormals(void)
{
    if (_mesh == NULL)
        return NULL;
    MFVec3f *v = _mesh->getNormals();
    
    if (v != NULL) {
        v =  new MFVec3f((float *)((MFVec3f *)v->copy())->getValues(), 
                         v->getSize());
    }
    return v;
}

MFInt32 *
MeshBasedNode::getSmoothNormalIndex(void)
{
    if (_mesh == NULL)
        return NULL;
    MFInt32 *i = _mesh->getNormalIndex();
    
    if (i != NULL) {
        i =  new MFInt32((int *)((MFInt32 *)i->copy())->getValues(), 
                         i->getSize());
    }
    return i;
}

void
MeshBasedNode::setTexCoordFromMesh(Node *ntexCoord)
{
    if (ntexCoord->getType() != VRML_TEXTURE_COORDINATE)
        return;
    if (_mesh == NULL)
        return;
    MFVec2f *v = _mesh->getTexCoords();
    if (v != NULL) {
        int field = ((NodeTextureCoordinate *)ntexCoord)->point_Field();
        _scene->setField(ntexCoord, field, v);
    }
}

int             
MeshBasedNode::write(int f, int indent) 
{
    int flags = _scene->getWriteFlags();
    if (flags & TRIANGULATE) {
        bool shouldBeIndexedTriangleSet = shouldConvertToIndexedTriangleSet();
        bool shouldBeTriangleSet = shouldConvertToTriangleSet();

/*
        if (shouldBeTriangleSet && !isFlat())
            shouldBeTriangleSet = false;
*/

        if (shouldBeTriangleSet) {
            Node *node = (NodeTriangleSet *)toTriangleSet();
            node->setVariableName(getVariableName());
            int ret = node->write(f, indent);
            node->unref();
            return ret;
        } else if (shouldBeIndexedTriangleSet) {
            Node *node = (NodeIndexedTriangleSet *)toIndexedTriangleSet();
            node->setVariableName(getVariableName());
            int ret = node->write(f, indent);
            node->unref();
            return ret;
        }
    }
    return Node::write(f, indent);
}

int             
MeshBasedNode::writeXml(int f, int indent) 
{
    int flags = _scene->getWriteFlags();
    if (flags & X3D_4_WONDERLAND) {
        if (meshDirty()) {
            createMesh(false);
            _meshDirty = false;
        }

        if (!_mesh) return -1;

        MFFloat *mfcolors = _mesh->getColors();
        MFColor *colors = NULL;
        if (mfcolors != NULL) {
            float *fcolors = (float *)mfcolors->getValues();
            colors = new MFColor(fcolors, mfcolors->getSize());
        }
        bool hasColors = (colors != NULL); 
        bool needMaterial = !hasColors;
        Node *node = toIndexedFaceSet(false, false);
        NodeIndexedFaceSet *faceSet = (NodeIndexedFaceSet *)node;
        float color[3] = { 0.8, 0.8, 0.8 };
        NodeColor *ncolor = (NodeColor *)_scene->createNode("Color");
        if ((hasColors && colors->getSFSize() == 1)) {
            const float *c = colors->getValue(0);
            for (int i = 0; i < 3; i++)
                color[i] = c[i];
        } else {
            if (hasParent()) {
                Node *parent = getParent();
                if (parent->getType() == VRML_SHAPE) {
                    NodeShape *shape = (NodeShape *)parent;
                    NodeAppearance *nappearance = (NodeAppearance *)
                          shape->appearance()->getValue();
                    if (nappearance &&
                        (nappearance->getType() == VRML_APPEARANCE)) {
                        NodeMaterial *nmaterial = (NodeMaterial *) 
                              nappearance->material()->getValue();
                        if (nmaterial && 
                            (nmaterial->getType() == VRML_MATERIAL)) {
                            const float *c = 
                                   nmaterial->diffuseColor()->getValue();
                            for (int i = 0; i < 3; i++)
                                color[i] = c[i];
                        }
                    } 
                }
            }
        }    
        int numVertices = _mesh->getVertices()->getSFSize();
        int errorID = -1;
        bool validColors = false;
        if (_mesh->colorPerVertex()) {
            if (hasColors)
                if (numVertices == colors->getSFSize())
                    validColors = true;
                else
                    errorID = IDS_WRONG_COLOR_SIZE_IN_WONDERLAND;
        } else if (hasColors)
            if (colors->getSFSize() > 1)
                errorID = IDS_NEED_COLOR_PER_VERTEX_IN_WONDERLAND;
        if (errorID != -1) {
            char string[256];
            swLoadString(errorID, string, 255);
            const char *name = NULL;
            if (hasName())
                name = getName();
            else
                name = getProto()->getName(true);
            swDebugf(string, name);
        }
        if (validColors)
            ncolor->color(colors);
        else {
            MFColor *uniqColours = new MFColor();
            for (int i = 0; i < numVertices; i++)
                uniqColours->insertSFValue(i, color);
            ncolor->color(uniqColours);
        }                    
        faceSet->color(new SFNode(ncolor));
        RET_ONERROR( node->NodeData::writeXml(f, indent) )
        node->unref();
        _meshDirty = true;
    } else
        RET_ONERROR( NodeData::writeXml(f, indent) )
    return 0;
}

Node *
MeshBasedNode::getConvertedNode(void)
{ 
    if (_convertedNode != NULL)
        return _convertedNode;
    int flags = _scene->getWriteFlags();
    if (flags & TRIANGULATE) {
        bool shouldBeIndexedTriangleSet = shouldConvertToIndexedTriangleSet();
        bool shouldBeTriangleSet = shouldConvertToTriangleSet();

        int meshFlags = MESH_WANT_NORMAL | MESH_WANT_TEX_COORD;

        if (shouldBeTriangleSet) {
            Node *node = (NodeTriangleSet *)toTriangleSet(meshFlags);
            if (node != NULL) {
                node->setVariableName(getVariableName());
                node->addParent(getParent(), getParentField());
            }
            return node;
        } else if (shouldBeIndexedTriangleSet) {
            Node *node = (NodeIndexedTriangleSet *)
                         toIndexedTriangleSet(meshFlags);
            if (node != NULL) {
                node->setVariableName(getVariableName());
                node->addParent(getParent(), getParentField());
            }
            return node;
        }
        if (getType() == X3D_TRIANGLE_SET) {
            NodeTriangleSet *node = (NodeTriangleSet *)this;
            if (node->normal()->getValue() == NULL)
                createNormalFromMesh(node);
            if (node->texCoord()->getValue() == NULL)
                createTexCoordFromMesh(node);
        } else if (getType() == X3D_INDEXED_TRIANGLE_SET) {
            NodeIndexedTriangleSet *node = (NodeIndexedTriangleSet *)this;
            if (node->normal()->getValue() == NULL)
                createNormalFromMesh(node);
            if (node->texCoord()->getValue() == NULL)
                createTexCoordFromMesh(node);
        }   
    } else if (flags & C_MESH) {

        bool shouldBeIndexedFaceSet = (getType() != VRML_INDEXED_FACE_SET);

        int meshFlags = MESH_WANT_NORMAL | MESH_WANT_TEX_COORD;

        if (shouldBeIndexedFaceSet) {
            Node *node = (NodeIndexedFaceSet *)toIndexedFaceSet(meshFlags);
            if (node != NULL) {
                node->setVariableName(getVariableName());
                node->addParent(getParent(), getParentField());
                _convertedNode = node;
            }
            return node;
        }

        if (getType() == VRML_INDEXED_FACE_SET) {
            NodeIndexedFaceSet *node = (NodeIndexedFaceSet *)this;
            if (node->normal()->getValue() == NULL)
                createNormalFromMesh(node);
            if (node->texCoord()->getValue() == NULL)
                createTexCoordFromMesh(node);
        }
    }
    return NULL; 
}       

#define writeAc3dRef(index) {                                            \
    int offset = face->getOffset() + index;                              \
    int ref = _mesh->getCoordIndex()->getValue(offset);                  \
    float texCoordX = 0;                                                 \
    float texCoordY = 0;                                                 \
    int texRef = ref;                                                    \
    if (_mesh->getTexCoordIndex() != NULL)                               \
        texRef =  _mesh->getTexCoordIndex()->getValue(offset);           \
    if (texRef < texCoords->getSFSize()) {                               \
        texCoordX = texCoords->getValue(texRef)[0];                      \
        texCoordY = texCoords->getValue(texRef)[1];                      \
    }                                                                    \
    RET_ONERROR( mywritef(f, "%d %f %f\n", ref, texCoordX, texCoordY) )  \
}

int
MeshBasedNode::writeAc3d(int f, int indent)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }

    if (_mesh == NULL)
        return 0;

    _mesh->optimize();

    NodeColor *color = (NodeColor *) getColorNode();

    NodeMaterial *nmaterial = getMaterial();
    NodeImageTexture *nimageTexture = getImageTexture();
    NodeTextureTransform *ntextureTransfrom = getTextureTransform();

    RET_ONERROR( mywritestr(f, "OBJECT poly\n") )

    if (hasName())
        RET_ONERROR( mywritef(f, "name \"%s\"\n", getName().getData()) )
        
    if (nimageTexture) {
        const char *texture = nimageTexture->url()->getValue(0);
        if (texture) {
            URL url;
            url.FromPath(texture);
            MyString filename = rewriteURL(url,url.GetDir(),_scene->getURL());
            bool convert = TheApp->GetAc3dExportConvert2Gif();
            char *file = strdup(filename);
            char *gifFile = (char *)malloc(strlen(file) + strlen(".gif") + 1);
            strcpy(gifFile, file);
#ifdef HAVE_IMAGE_CONVERTER
            if (convert) {                
                char *dotptr = strrchr(gifFile, '.');
                if (dotptr != NULL) {
                    // shorten file, if the dot looks like belonging to a extension
                    if (dotptr >= (gifFile - strlen(".jpeg")))   
                        *dotptr = (char)0;
                }
                strcat(gifFile, ".gif");
                int pid = fork();
                if (pid == 0) {
                    execl(HAVE_IMAGE_CONVERTER, HAVE_IMAGE_CONVERTER, 
                          file, gifFile, (char *)NULL);
                } else {
                   int status;
                   waitpid(-1, &status, 0);
                }
            }
#endif                    
            RET_ONERROR( mywritef(f, "texture \"%s\"\n", gifFile) )
            free(file);
            free(gifFile);
            if (ntextureTransfrom) {
                RET_ONERROR( mywritef(f, "texrep %f %f\n", 
                                      ntextureTransfrom->scale()->getValue(0),
                                      ntextureTransfrom->scale()->getValue(1)) )
                RET_ONERROR( mywritef(f, "texoff %f %f\n", 
                      ntextureTransfrom->translation()->getValue(0),
                      ntextureTransfrom->translation()->getValue(1)) )
                /* is there a secrect keyword for texture rotation ? 
                RET_ONERROR( mywritef(f, "???texrot??? %f\n", 
                                      ntextureTransfrom->rotation()->getValue)) )
                */
            }
        }
    }
    MFFloat *colors = _mesh->getColors();
    MFInt32 *colorIndex = _mesh->getColorIndex();
    bool colorPerVertexProblem = _mesh->colorPerVertex();
    if (!colors || (colors && (colors->getSize() == 0)))
        if (colorPerVertexProblem)
            colorPerVertexProblem = false;
    if (colorPerVertexProblem) {
        const char *name = "";
        if (hasName())
            name = getName();
        else
            name = getProto()->getName(_scene->isX3d());
        swDebugf("Warning: color node of %s node can not ", name);
        swDebugf("be exported to AC3D cause of colorPerVertex TRUE\n");
    }
    RET_ONERROR( mywritef(f, "crease %f\n", RAD2DEG(_mesh->creaseAngle())) )
    MFVec3f *vertices = getVertices();
    RET_ONERROR( mywritef(f, "numvert %d\n", vertices->getSFSize()) )
    Matrix matrix;
    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) matrix);
    int i;
    for (i = 0; i < vertices->getSFSize(); i++) {
        Vec3f v(vertices->getValue(i)[0],
                vertices->getValue(i)[1],
                vertices->getValue(i)[2]);
        v = matrix * v;
        RET_ONERROR( mywritef(f, "%f %f %f\n",v.x, v.y,v.z) )
    }
    RET_ONERROR( mywritef(f, "numsurf %d\n", _mesh->getNumFaces()) )
    int surfaceFlags = 0x00; // polygon
    surfaceFlags += _mesh->solid() ? 1 << 4 : 1 << 5; // strange flat/smooth shading rules for the "virtual accoustics" program
    MFVec2f* texCoords = NULL;
    if (_mesh->getTexCoords() == NULL)
        texCoords = _mesh->generateTextureCoordinates();
    else
        texCoords = _mesh->getTexCoords();
    for (i = 0; i < _mesh->getNumFaces(); i++) {
        RET_ONERROR( mywritef(f, "SURF 0x%x\n", surfaceFlags) )
        int index = -1; 
        bool materialToWrite = true;
        if (!colorPerVertexProblem) {
            if (colorIndex && (colorIndex->getSize() > 0)) {
                if (i < colorIndex->getSize())
                    index = colorIndex->getValue(i);
            } else
                index = i;
            int materialIndex = 0;
            if ((index >= 0) && color)
                materialIndex = color->getAc3dMaterialIndex(index);
            if ((index >= 0) && colors && (index < colors->getSize()) && 
                (materialIndex > -1)) { 
                RET_ONERROR( mywritef(f, "mat %d\n", materialIndex) )
                materialToWrite = false;
            }
        }
        if (materialToWrite)
            if (nmaterial)
                RET_ONERROR( mywritef(f, "mat %d\n", 
                                      nmaterial->getAc3dMaterialIndex()) )
            else
                RET_ONERROR( mywritef(f, "mat %d\n", 
                                      _scene->getAc3dEmptyMaterial()) )

        Face *face = _mesh->getFace(i);
        RET_ONERROR( mywritef(f, "refs %d\n", face->getNumVertices()) )
        if (_mesh->ccw())
            for (int j = 0; j < face->getNumVertices(); j++)
                writeAc3dRef(j)
        else
            for (int j = face->getNumVertices() - 1; j >= 0; j--)
                writeAc3dRef(j)
    }
    RET_ONERROR( mywritestr(f, "kids 0\n") )
    _meshDirty = true;
    return 0;
}

#define UNKNOWN_MATERIAL "TOTREF"

int
MeshBasedNode::writeCattGeo(int filedes, int indent)
{ 
    char *path = _scene->getCattGeoPath();
    int len = strlen(path)+256;
    char *filename = new char[len];
    mysnprintf(filename, len - 1, "%sShape%d.geo", path,
               _scene->getAndIncCattGeoFileCounter());
    int f = open(filename, O_WRONLY | O_CREAT, 00666);
    if (f == -1)
        return -1;
    delete [] filename;

    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }

    if (_mesh == NULL) 
        return 0;

    _mesh->optimize();

    Node *parent = NULL;
    if (hasParent())
        parent = getParent();

    NodeMaterial *nmaterial = getMaterial();
   
    RET_ONERROR( mywritestr(f, "OBJECT") )
    RET_ONERROR( mywritestr(f, swGetLinefeedString()) )

    if (hasName())
        RET_ONERROR( mywritef(f, "; Shape with vrml97 DEF name %s: ", 
                              getName().getData()) )
    RET_ONERROR( mywritestr(f, swGetLinefeedString()) )

    RET_ONERROR( mywritestr(f, "CORNERS") )
    RET_ONERROR( mywritestr(f, swGetLinefeedString()) )
    MFVec3f *vertices = getVertices();
    Matrix matrix;
    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) matrix);
    int cornerCounter = _scene->getCattGeoCornerCounter();
    int i;
    for (i = 0; i < vertices->getSFSize(); i++) {
        Vec3f v(vertices->getValue(i)[0], 
                vertices->getValue(i)[1], 
                vertices->getValue(i)[2]);
        v = matrix * v;        
        RET_ONERROR( mywritef(f, "%d %f %f %f%s", 
                              _scene->getAndIncCattGeoCornerCounter(),
                              v.x, v.z, v.y, swGetLinefeedString()) )
    }

    RET_ONERROR( mywritestr(f, "PLANES") )
    RET_ONERROR( mywritestr(f, swGetLinefeedString()) )
    bool meshCcw = _mesh->ccw();
    // write both sides, if solid is false
    int sides = _mesh->solid() ? 1 : 2;
    for (int side = 1; side < (sides + 1); side++) {
        if (side == 2)
                meshCcw = !meshCcw;
        for (i = 0; i < _mesh->getNumFaces(); i++) {
            Face *face = _mesh->getFace(i);
            RET_ONERROR( mywritef(f, "[%d ", 
                                  _scene->getAndIncCattGeoPlaneCounter()) )
            if (nmaterial)
                RET_ONERROR( mywritestr(f, nmaterial->getExportMaterialName(
                      UNKNOWN_MATERIAL)) )
            else
                RET_ONERROR( mywritestr(f, UNKNOWN_MATERIAL) )
            RET_ONERROR( mywritestr(f, " / ") )
            MFInt32 *mfcoordIndex = _mesh->getCoordIndex();
            if (meshCcw)
                for (int j = face->getNumVertices() - 1; j >= 0; j--) {
                    int ref = mfcoordIndex->getValue(face->getOffset() + j);
                    RET_ONERROR( mywritef(f, "%d ", ref + cornerCounter) )
                }
            else 
                for (int j = 0; j < face->getNumVertices(); j++) {
                    int ref = mfcoordIndex->getValue(face->getOffset() + j);
                    RET_ONERROR( mywritef(f, "%d ", ref + cornerCounter) )
                }
            RET_ONERROR( mywritestr(f, "/ ") )
            if (nmaterial)
                RET_ONERROR( mywritestr(f, nmaterial->getExportMaterialName(
                      UNKNOWN_MATERIAL)) )
            else
                RET_ONERROR( mywritestr(f, UNKNOWN_MATERIAL) )
            RET_ONERROR( mywritestr(f, " ]") )
            RET_ONERROR( mywritestr(f, swGetLinefeedString()) )
        }            
    }

    _meshDirty = true;
    return swTruncateClose(f);
}

int
MeshBasedNode::writeLdrawDat(int f, int indent)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }

    if (_mesh == NULL)
        return 0;

    if (_mesh->getMaxNumberEdgesPerFace() > 4)
        _mesh->triangulate();

    MFVec3f *vertices = getVertices();
    MFInt32 *mfcoordIndex = _mesh->getCoordIndex();
    Matrix matrix;
    glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) matrix);
    RET_ONERROR( mywritestr(f, "0 //") )
    if (hasName())
        RET_ONERROR( mywritef(f, " %s", (const char *)getName()))
    RET_ONERROR( mywritef(f, " %s\n", (const char *)getProto()->getName(false)))
    for (int i = 0; i < _mesh->getNumFaces(); i++) {
        Face *face = _mesh->getFace(i);
        if (face->getNumVertices() == 4) 
            RET_ONERROR( mywritestr(f, "4 ") )
        else if (face->getNumVertices() == 3)
            RET_ONERROR( mywritestr(f, "3 ") )
        else 
            continue;
        int color = _scene->getCurrentLdrawColor();
        if (color == -1)
            color = LDRAW_CURRENT_COLOR;
        else {
            Node *node = NULL;
            if (getType() == VRML_ELEVATION_GRID)
                node = ((NodeElevationGrid *)this)->color()->getValue();
            else if (getType() == VRML_GEO_ELEVATION_GRID)
                node = ((NodeGeoElevationGrid *)this)->color()->getValue();
            else if (getType() == VRML_INDEXED_FACE_SET)
                node = ((NodeIndexedFaceSet *)this)->color()->getValue();
            NodeColor *colorNode = (NodeColor *)node;

            MFFloat *colors = _mesh->getColors();
            MFInt32 *colorIndex = _mesh->getColorIndex();
            bool colorPerVertexProblem = _mesh->colorPerVertex();
            if (!colors || (colors && (colors->getSize() == 0)))
                if (colorPerVertexProblem)
                    colorPerVertexProblem = false;
            int index = -1; 
            if (colorPerVertexProblem) {
                const char *name = "";
                if (hasName())
                    name = getName();
                else
                    name = getProto()->getName(_scene->isX3d());

                swDebugf("Warning: color node of %s node can not", name);
                swDebugf("be exported to Ldraw cause of colorPerVertex");
                swDebugf(" TRUE\n");
            }
            else if (colorIndex && (colorIndex->getSize() > 0)) {
                if (i < colorIndex->getSize())
                    index = colorIndex->getValue(i);
                else
                    index = i;
                if ((index >= 0) && colorNode)
                    color = colorNode->getLdrawColor(index);
                if (color < 0)
                    color = LDRAW_CURRENT_COLOR;
            }
        }
        RET_ONERROR( mywritef(f, "%d ", color) )
        for (int j = 0; j < face->getNumVertices(); j++) {
             int ref = mfcoordIndex->getValue(face->getOffset() + j);
             Vec3f v(vertices->getValue(ref)[0],
                     vertices->getValue(ref)[1],
                     vertices->getValue(ref)[2]);
             v = matrix * v;
             RET_ONERROR( mywritef(f, " %f %f %f",  v.z * LDRAW_SCALE, 
                                                   -v.y * LDRAW_SCALE, 
                                                    v.x * LDRAW_SCALE) )
        }
        RET_ONERROR( mywritestr(f, "\n") )
    }
    _meshDirty = true;
    return 0;
}

void
MeshBasedNode::smoothNormals(bool optimizeNormals)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return;
    _mesh->smoothNormals(optimizeNormals);
}

void
MeshBasedNode::flip(int index)
{
    switch (getType()) {
      case VRML_BOX:
      case VRML_CYLINDER:
      case VRML_SPHERE:
        return;
      default:
        {
        Node *faceSet = _scene->convertToIndexedFaceSet(this);
        if (faceSet != NULL)
            faceSet->flip(index);
        }
     }
}

void
MeshBasedNode::swap(int fromTo)
{
    Node *faceSet = _scene->convertToIndexedFaceSet(this);
    if (faceSet != NULL)
        faceSet->swap(fromTo);
}

void            
MeshBasedNode::setCreaseAngle(float angle)
{ 
    if (_mesh)
        _mesh->setCreaseAngle(angle); 
}

void
MeshBasedNode::optimizeMesh(MFVec3f *newCoord, MFInt32 *newCoordIndex)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return;
    MFInt32 *indices = _mesh->optimizeCoordIndex();
    _mesh->optimizeVertices(newCoord, newCoordIndex);
}

Mesh *           
MeshBasedNode::triangulateMesh(void)
{
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
#ifdef HAVE_GLUNEWTESS
    return _mesh->triangulate();
#else
    return NULL;
#endif
}

MFVec3f *
MeshBasedNode::getCoordFromMesh()
{ 
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
    return _mesh->getVertices(); 
}

MFInt32 *
MeshBasedNode::getCoordIndexFromMesh()
{ 
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return NULL;
    return _mesh->getCoordIndex(); 
}

bool
MeshBasedNode::isFlat(void)
{ 
    if (meshDirty()) {
        createMesh();
        _meshDirty = false;
    }
    if (_mesh == NULL)
        return false;
    return (_mesh->creaseAngle() == 0.0f);
}

