/*
 * NodeNurbsPositionInterpolator.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 <stdio.h>
#include "stdafx.h"

#include "NodeNurbsPositionInterpolator.h"
#include "NodeCoordinate.h"
#include "Proto.h"
#include "DuneApp.h"
#include "Scene.h"
#include "FieldValue.h"
#include "Field.h"
#include "SFFloat.h"
#include "SFInt32.h"
#include "SFBool.h"
#include "Vec2f.h"
#include "MoveCommand.h"

ProtoNurbsPositionInterpolator::ProtoNurbsPositionInterpolator(Scene *scene)
  : Proto(scene, "NurbsPositionInterpolator")
{
    addEventIn(SFFLOAT, "set_fraction", EIF_RECOMMENDED);
    dimension.set(
          addExposedField(SFINT32, "dimension", new SFInt32(0)));
    setFieldFlags(dimension, FF_VRML_ONLY);
    keyValue.set(
          addExposedField(MFVEC3F, "keyValue", new MFVec3f()));
    setFieldFlags(keyValue, FF_VRML_ONLY | EIF_RECOMMENDED);
    controlPoint.set(
          addExposedField(SFNODE, "controlPoint", new SFNode(NULL), 
                          COORDINATE_NODE));
    setFieldFlags(controlPoint, FF_X3D_ONLY);
    keyWeight.set(
          addExposedField(MFFLOAT, "keyWeight", new MFFloat(), 
                          new SFFloat(0.0f), NULL, "weight"));
    knot.set(
          addField(MFFLOAT, "knot", new MFFloat()));
    order.set(
          addField(SFINT32, "order", new SFInt32(4), new SFInt32(2)));
    getField(order)->addX3dDefault(new SFInt32(3));
    addEventOut(SFVEC3F, "value_changed", EOF_RECOMMENDED);
}

Node *
ProtoNurbsPositionInterpolator::create(Scene *scene)
{
    return new NodeNurbsPositionInterpolator(scene, this); 
}

NodeNurbsPositionInterpolator::NodeNurbsPositionInterpolator(Scene *scene, Proto *def)
  : Node(scene, def)
{
    _nurbsCurveDirty = true;
    _nurbsCurve = (NodeNurbsCurve *) scene->createNode("NurbsCurve");
    _nurbsCurve->setInternal(true);

}

NodeNurbsPositionInterpolator::~NodeNurbsPositionInterpolator()
{
    delete _nurbsCurve;
}

MFVec3f *
NodeNurbsPositionInterpolator::getControlPoints(void)
{
    if (_scene->isX3d()) {
        Node *pointNode = controlPoint()->getValue();
        if (pointNode)
            return ((NodeCoordinate *)pointNode)->point();
    }
    return keyValue();
}

void
NodeNurbsPositionInterpolator::setControlPoints(const MFVec3f *points)
{
    if (_scene->isX3d()) {
        NodeCoordinate *coord = (NodeCoordinate *)controlPoint()->getValue();
        if (coord != NULL)
            _scene->setField(coord, coord->point_Field(), new MFVec3f(points));
    } else
        _scene->setField(this, keyValue_Field(), new MFVec3f(points));
}

void
NodeNurbsPositionInterpolator::createNurbsCurve()
{
    int i;
    float *points = new float[getControlPoints()->getSize()];
    for (i = 0; i < getControlPoints()->getSize(); i++)
         points[i] = getControlPoints()->getValues()[i];
    setControlPoints(new MFVec3f(points, getControlPoints()->getSize()));
    float *weights = new float[keyWeight()->getSize()];
    for (i = 0; i < keyWeight()->getSize(); i++)
         weights[i] = keyWeight()->getValues()[i];
    _nurbsCurve->weight(new MFFloat(weights, keyWeight()->getSize()));
    float *knots = new float[knot()->getSize()];
    for (i = 0; i < knot()->getSize(); i++)
         knots[i] = knot()->getValues()[i];
    _nurbsCurve->knot(new MFFloat(knots, knot()->getSize()));
    _nurbsCurve->order(new SFInt32(order()->getValue()));
    const Vec3f *chain = _nurbsCurve->getChain();
    int chainLength = _nurbsCurve->getChainLength();
    float *fchain = new float[chainLength * 3];
    for (i = 0; i < chainLength; i++) {
         fchain[i * 3    ] = chain[i].x;
         fchain[i * 3 + 1] = chain[i].y;
         fchain[i * 3 + 2] = chain[i].z;
    }   
    _nurbsCurveDirty = false;
}

int
NodeNurbsPositionInterpolator::writeProto(int f)
{
    RET_ONERROR( mywritestr(f ,"EXTERNPROTO NurbsPositionInterpolator[\n") )    
    TheApp->incSelectionLinenumber();
    RET_ONERROR( writeProtoArguments(f) );
    RET_ONERROR( mywritestr(f ," ]\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ,"[\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:web3d:vrml97:node:NurbsPositionInterpolator\",\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:inet:blaxxun.com:node:NurbsPositionInterpolator\",\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:ParaGraph:NurbsPositionInterpolator\",\n") )
    TheApp->incSelectionLinenumber();
#ifdef HAVE_VRML97_AMENDMENT1_PROTO_URL
    RET_ONERROR( mywritestr(f ," \"") )
    RET_ONERROR( mywritestr(f ,HAVE_VRML97_AMENDMENT1_PROTO_URL) )
    RET_ONERROR( mywritestr(f ,"/NurbsPositionInterpolatorPROTO.wrl") )
    RET_ONERROR( mywritestr(f ,"\"\n") )
    TheApp->incSelectionLinenumber();
#else
    RET_ONERROR( mywritestr(f ," \"NurbsPositionInterpolatorPROTO.wrl\",\n") )
    TheApp->incSelectionLinenumber();
#endif
    RET_ONERROR( mywritestr(f ," \"http://129.69.35.12/dune/docs/vrml97Amendment1/NurbsPositionInterpolatorPROTO.wrl\"\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ,"]\n") )
    TheApp->incSelectionLinenumber();
    return 0;
}

int             
NodeNurbsPositionInterpolator::write(int filedes, int indent) 
{
    if (_scene->isPureVRML()) {
/*        
        Node *node = _nurbsCurve->toPositionInterpolator();
        if (node == NULL) 
           return 1;

        // fixme: read Route connects of node and 
        // build routes to new node instead

        RET_ONERROR( node->write(filedes, indent) )
        node->unref();
*/
    } else
        RET_ONERROR( NodeData::write(filedes, indent) )
    return 0;
}

void
NodeNurbsPositionInterpolator::setField(int index, FieldValue *value)
{
    _nurbsCurveDirty = true;
    Node::setField(index, value);
}

void
NodeNurbsPositionInterpolator::draw()
{
    if (_nurbsCurveDirty) {
        createNurbsCurve();
        _nurbsCurveDirty = false;
    }

    if (!_nurbsCurve) return;

    _nurbsCurve->draw();
}

void  
NodeNurbsPositionInterpolator::drawHandles()
{
    if (_nurbsCurveDirty) {
        createNurbsCurve();
        _nurbsCurveDirty = false;
    }

    if (!_nurbsCurve) return;

    _nurbsCurve->drawHandles();
}

Vec3f
NodeNurbsPositionInterpolator::getHandle(int handle, int *constraint,
                                         int *field)
{
    *field = keyValue_Field() ;

    if (handle >= 0 && handle < getControlPoints()->getSize() / 3) {
        Vec3f ret((Vec3f)getControlPoints()->getValue(handle) / 
                   keyWeight()->getValue(handle));
        return ret;
    } else {
        return Vec3f(0.0f, 0.0f, 0.0f);
    }
}

void
NodeNurbsPositionInterpolator::setHandle(int handle, const Vec3f &v) 
{
    MFVec3f    *oldValue = getControlPoints();
    MFVec3f    *newValue = (MFVec3f *)oldValue->copy();

    if (handle >= 0 && handle < oldValue->getSize() / 3) {
        Vec3f v2 = v * keyWeight()->getValue(handle); 
        _nurbsCurveDirty = true;
        newValue->setValue(handle * 3, v2.x);
        newValue->setValue(handle * 3+1, v2.y);
        newValue->setValue(handle * 3+2, v2.z);
        setControlPoints(newValue);
    }
}

void
NodeNurbsPositionInterpolator::flip(int index)
{
    if (getControlPoints())
        getControlPoints()->flip(index);
    _nurbsCurveDirty = true;    
}

void
NodeNurbsPositionInterpolator::swap(int fromTo)
{
    if (getControlPoints())
        getControlPoints()->swap(fromTo);
    _nurbsCurveDirty = true;    
}

Node   *
NodeNurbsPositionInterpolator::toNurbsCurve(void)
{
  NodeNurbsCurve *node = (NodeNurbsCurve *) _scene->createNode("NurbsCurve");
  
  int i;
  float *tmpControlPoints = new float[getControlPoints()->getSize()];
  float *tmpWeights = new float[keyWeight()->getSize()];
  float *tmpKnots = new float[knot()->getSize()];
  int tmpOrder = order()->getValue();  
  
  for(i=0; i<getControlPoints()->getSize(); i++){
    tmpControlPoints[i] = getControlPoints()->getValues()[i];
  }

  for(i=0; i<(keyWeight()->getSFSize()); i++){
    tmpWeights[i] = keyWeight()->getValue(i);
  }
  
  for(i=0; i<(knot()->getSFSize()); i++){
    tmpKnots[i] = knot()->getValue(i);
  }
    
  node->knot(new MFFloat(tmpKnots, knot()->getSFSize()));
  node->setField(node->order_Field(), new SFInt32(tmpOrder));
  node->setControlPoints(new MFVec3f(tmpControlPoints, keyValue()->getSize()));
  node->weight(new MFFloat(tmpWeights, keyWeight()->getSFSize()));

  return node;
}

Node *
NodeNurbsPositionInterpolator::convert2X3d(void)
{
    MFVec3f *points = keyValue();
    NodeCoordinate *node = (NodeCoordinate *)_scene->createNode("Coordinate");
    int len = points->getSize();
    float *floats = new float[len];
    for (int i = 0; i < len; i++)
        floats[i] = points->getValues()[i];
    node->point(new MFVec3f(floats, len));
    SFNode *old = this->controlPoint();
    if (old != NULL)
        if (old->getValue() != NULL)
            _scene->execute(new MoveCommand(old->getValue(), 
                                            this, controlPoint_Field(),
                                            NULL, -1));
    _scene->execute(new MoveCommand(node, NULL, -1, this, 
                                    controlPoint_Field()));
    _scene->changeRoutes(node, node->point_Field(), this, keyValue_Field(),
                         false);
    _scene->UpdateViews(NULL, UPDATE_REDRAW, NULL);
    return NULL;
}

Node *
NodeNurbsPositionInterpolator::convert2Vrml(void) 
{
    NodeCoordinate *node = (NodeCoordinate *)controlPoint()->getValue();
    if (node != NULL) {
        int len = node->point()->getSize();
        float *floats = new float[len];
        for (int i = 0; i < len; i++)
            floats[i] = node->point()->getValues()[i];
        keyValue(new MFVec3f(floats, len));
        _scene->changeRoutes(this, keyValue_Field(), 
                             node, node->point_Field(), false);
        _scene->execute(new MoveCommand(node, this, controlPoint_Field(),
                                        NULL, -1));
    } else
        keyValue(new MFVec3f());
    _scene->UpdateViews(NULL, UPDATE_REDRAW, NULL);
    return NULL;
}


