/*
 * MeshMorphingNode.cpp
 *
 * Copyright (C) 2005 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 "MeshMorphingNode.h"
#include "Mesh.h"
#include "Field.h"
#include "Scene.h"
#include "NodeIndexedFaceSet.h"
#include "NodeCoordinateInterpolator.h"
#include "NodeNormalInterpolator.h"
#include "EventOut.h"

int             
MeshMorphingNode::write(int f, int indent) 
{
    int flags = _scene->getWriteFlags();
    if (flags & PURE_VRML97) {
        _meshDirty = true;
        Node *node = NULL;
        if (_scene->getWriteKanimNow())
            node = toIndexedFaceSet(TheApp->GetNormalsOnPureVRML97(), false);
        else {
            node = toIndexedFaceSet(TheApp->GetNormalsOnPureVRML97(), true);
            if (!writeCoordinateInterpolator(node, f, indent, flags, true)) {
                node = toIndexedFaceSet(TheApp->GetNormalsOnPureVRML97(), 
                                        false);
                writeCoordinateInterpolator(node, f, indent, flags, false);
            }
        }
        if (isX3dXml(flags))
            RET_ONERROR( node->writeXml(f, indent) )
        else
            RET_ONERROR( node->write(f, indent) )
        node->unref();
        _meshDirty = true;
    } else {
        if (isX3dXml(flags))
            RET_ONERROR( MeshBasedNode::writeXml(f, indent) )
        else
            RET_ONERROR( MeshBasedNode::write(f, indent) )
    }
    return 0;
}

void 
MeshMorphingNode::findInterpolators(InterpolatorInfo& info)
{
    Array<int> fieldList;
    int i;
    for (i = 0; i < _proto->getNumFields(); i++) {
        int type = _proto->getField(i)->getType();
        if (typeDefaultValue(type)->supportAnimation(_scene->isX3d()))
            fieldList.append(i);
    }
    
    for (i = 0; i < fieldList.size(); i++) {
        Interpolator *inter = _scene->findUpstreamInterpolator(this, 
                                                               fieldList[i]);
        if (inter != NULL) {
            info.interpolator.append(inter);
            info.field.append(fieldList[i]);
        }
    }
}


bool
MeshMorphingNode::writeCoordinateInterpolator(Node *node, int filedes, 
                                              int indent, int flags,
                                              bool optimize)
{
    // find interpolators which have routes to the morphing node
    InterpolatorInfo info;

    findInterpolators(info);

    if (info.interpolator.size() == 0) {
        // nothing to do
        return true;
    }

    // get name of used input of interpolators
    // only the first found input is used
    MyString interInputName;
    MyString interInputEventName;
    bool foundInterInput = false;
    for (int k = 0; k < info.interpolator.size(); k++) {
        Node *node = info.interpolator[k];
        int eventIn = node->lookupEventIn("set_fraction", false);
        if (eventIn == INVALID_INDEX)
            continue;

        // use first eventIn found to drive Interpolator
        SocketList::Iterator *iter= node->getInput(eventIn).first();
        if (iter && iter->item().getNode()) {
            Node *src = iter->item().getNode();
            interInputName = src->getName();
            int field = iter->item().getField();
            interInputEventName = src->getProto()->getEventOut(field)->
                                       getName(src->getScene()->isX3d());
            foundInterInput = true;
            break;
        }
    }
        
    // sort list of keys
    Array<float> keys;
    Array<float> coordKeyValues;
    Array<float> normalKeyValues;
    bool foundMinKey = false;
    float currentKey = 0;
    // start list of keys with 0 
    keys.append(currentKey);
    do {
       foundMinKey = false;
       float minKey = 0;
       for (int i = 0; i < info.interpolator.size(); i++)
           for (int j = 0; j < info.interpolator[i]->getNumKeys(); j++)
               if (info.interpolator[i]->getKey(j) > currentKey)
                   if (foundMinKey == false) {
                       minKey = info.interpolator[i]->getKey(j);
                       foundMinKey = true;
                   } else if (info.interpolator[i]->getKey(j) < minKey)
                       minKey = info.interpolator[i]->getKey(j);
       if (foundMinKey) {
           keys.append(minKey);
           currentKey = minKey;
       }
    } while (foundMinKey);


    // get data from Interpolators and generate mesh

    void *data = initializeData();
    if (data == NULL)
        return true;
    for (int i = 0; i < keys.size(); i++) {
        for (int j = 0; j < info.interpolator.size(); j++) {
            Interpolator *inter = info.interpolator[j];
            int field = info.field[j];
            if (inter != NULL)
                loadDataFromInterpolators(data, inter, field, keys[i]);
        }

        createMeshFromData(data, optimize);

        // check if coordIndex of optimized mesh is identical to optimized node
        if (optimize) {
            MFInt32 *micoordIndex = ((NodeIndexedFaceSet *)node)->coordIndex();
            if (!micoordIndex->equals(_mesh->getCoordIndex()))
                return false;
        }

        MFVec3f *vertices = _mesh->getVertices();
        for (int k = 0; k < vertices->getSize(); k++)
            coordKeyValues.append(vertices->getValues()[k]);
        if (TheApp->GetNormalsOnPureVRML97()) {
            MFVec3f *normals = getSmoothNormals();
            for (int k = 0; k < normals->getSize(); k++)
                normalKeyValues.append(normals->getValues()[k]);
        }
    }
    finalizeData(data);
    data = NULL;    


    NodeCoordinateInterpolator *coordInter = (NodeCoordinateInterpolator *) 
          _scene->createNode("CoordinateInterpolator");

    // write CoordinateInterpolator
    MFFloat *mfkeys = new MFFloat((float *)keys.getData(), keys.size());      
    coordInter->key((MFFloat *)mfkeys->copy());

    MFVec3f *mfCoordKeyValues = new MFVec3f((float *)coordKeyValues.getData(),
                                             coordKeyValues.size());
    coordInter->keyValue((MFVec3f *)mfCoordKeyValues->copy());
    _scene->addDelayedWriteNode(coordInter); 


    // write route
    Node *ncoord = ((NodeIndexedFaceSet *)node)->coord()->getValue();
    _scene->addRouteString(_scene->createRouteString(
           coordInter->getName(true), "value_changed", 
           ncoord->getName(true), "set_point"));
    if (foundInterInput)
        _scene->addEndRouteString(_scene->createRouteString(
               interInputName, interInputEventName,
               coordInter->getName(true), "set_fraction"));

    if (TheApp->GetNormalsOnPureVRML97()) {
        // write NormalInterpolator
        NodeNormalInterpolator *normalInter = (NodeNormalInterpolator *) 
               _scene->createNode("NormalInterpolator");
    
        normalInter->key((MFFloat *)mfkeys->copy());

        MFVec3f *mfNormalKeyValues = new MFVec3f((float *)
                                                 normalKeyValues.getData(),
                                                 normalKeyValues.size());
        normalInter->keyValue((MFVec3f *)mfNormalKeyValues->copy());
        _scene->addDelayedWriteNode(normalInter); 

        // write Route
        Node *nnormal = ((NodeIndexedFaceSet *)node)->normal()->getValue();
        _scene->addRouteString(_scene->createRouteString(
              normalInter->getName(true), "value_changed", 
              nnormal->getName(true), "set_vector"));
        if (foundInterInput)
            _scene->addEndRouteString(_scene->createRouteString(
                  interInputName, interInputEventName, 
                  normalInter->getName(true), "set_fraction"));        
    }   
    return true;
} 

