
#include <WFX/scene/X3DLoader.h>
#include <tinyxml/tinyxml.h>

#include <WFX/scene/TriMesh.h>
#include <WFX/util/StringUtils.h>

using namespace WFX;
using namespace WFX::lang;
using namespace WFX::scene;
using namespace WFX::math;
using namespace WFX::util;
using namespace std;

X3DLoader::X3DLoader() {
}

Node* X3DLoader::load(const WFX::io::File& f) {
    Node* r = NULL;
    try {
        X3DLoader l;
        r = l.loadFromFile(f); 
    } catch (WFXException& e) {
        RETHROW(e, "unable to load X3D File: " << f.toString());
    }

    return r;
}

Node* X3DLoader::loadFromFile(const WFX::io::File& f) {
    if (!f.isFile()) 
        THROW("Not a file: " << f.toString());

    Node* r = NULL;

    LOG("parsing X3D File: " << f.toString());
    //parse
    TiXmlDocument doc(f.toString().data());
    if (!doc.LoadFile()) {
        THROW("Error parsing file " << f.toString());
    }
    TiXmlElement* root = doc.RootElement();
  
    //check for X3D root node
    std::string val = root->Value();
    if (val != "X3D") {
        THROW("RootNode not X3D but " << val);
    }
    //iterate over all child nodes
    TiXmlNode* child = NULL;
    while ((child = root->IterateChildren(child))) {
        val = child->Value();
        if (val == "Scene") {
            //Scene?
            r = parseScene(child->ToElement());
        } else {
            //unknown node
            LOG("X3DLoader -> Ignoring node " << val);
        }
    }
  
    return r;
}

Node* X3DLoader::parseScene(TiXmlElement* e) {
    LOG("parsing Scene...");

    Node* r = new Node();

    std::string val;
    //iterate over children
    TiXmlNode* child = NULL;
    while ((child = e->IterateChildren(child))) {
        val = child->Value();
        if (val == "Transform") {
            r->addChild(parseTransform(child->ToElement()));
        } else {
            LOG("X3DLoader::parseScene -> ignoring node " << val);
        }
    }

    return r;
}

Spatial* X3DLoader::parseTransform(TiXmlElement* e) {
    LOG("parsing Transform");

    Spatial* r = new Node();
    
    std::string val;
    //iterate over children
    TiXmlNode* child = NULL;
    while ((child = e->IterateChildren(child))) {
        val = child->Value();
        if (val == "Shape") {
            r = parseShape(child->ToElement());
        } else {
            LOG("X3DLoader::parseTransform -> ignoring node " << val);
        }
    }
    
    //parse transformation info
    string transstr = e->Attribute("translation");
    Vector3 trans;
    trans.parseStr(transstr);
    r->setLocalTranslation(trans);
    
    return r;
}

Spatial* X3DLoader::parseShape(TiXmlElement* e) {
    LOG("parsing Shape");
    Spatial* r = NULL;

    std::string val;
    //iterate over children
    TiXmlNode* child = NULL;
    while ((child = e->IterateChildren(child))) {
        val = child->Value();
        if (val == "IndexedFaceSet") {
            r = parseIndexedFaceSet(child->ToElement());
        } else {
            LOG("X3DLoader::parseShape -> ignoring node " << val);
        }
    }

    return r;
}

Spatial* X3DLoader::parseIndexedFaceSet(TiXmlElement* e) {
    LOG("parsing IndexedFaceSet");
    TriMesh* r = new TriMesh();

    std::string val;
    //iterate over children
    TiXmlNode* child = NULL;
    while ((child = e->IterateChildren(child))) {
        val = child->Value();
        if (val == "Coordinate") {
            //parse coordinate vectors
            std::string pointstr = child->ToElement()->Attribute("point");
            vector<Vector3> coords;
            StringUtils::parseVector3V(pointstr, coords);
            r->VertexCount = coords.size();
            r->Vertices = new Vector3[r->VertexCount];
            vector<Vector3>::iterator it;
            int i = 0;
            for (it = coords.begin(); it!=coords.end(); it++) {
                r->Vertices[i] = *(it);
                i++;
            }
        } else {
            LOG("X3DLoader::parseIndexedFaceSet -> ignoring node " << val);
        }
    }
    //parse coordinate indices
    std::string cistr = e->Attribute("coordIndex");
    std::vector<Int> iv;
    std::vector<Int> fv;
    std::vector<UInt> conn;
    StringUtils::parseIntV(cistr, iv);
    std::vector<Int>::iterator it;
    for (it = iv.begin(); it!=iv.end(); it++) {
        if (*(it)>=0) {
            fv.push_back(*(it));
        } else {
            r->FaceCount++;
            conn.push_back(fv[0]);
            conn.push_back(fv[1]);
            conn.push_back(fv[2]);
            if (fv.size()>3) {
                r->FaceCount++;
                conn.push_back(fv[0]);
                conn.push_back(fv[2]);
                conn.push_back(fv[3]);
            }
            fv.clear();
        }
    }
    r->Connectivity = new UInt[r->FaceCount*3];
    {
        std::vector<UInt>::iterator it;
        UInt cnt = 0;
        for (it = conn.begin(); it!=conn.end(); it++) {
            r->Connectivity[cnt] = *(it);
            cnt++;
        }
    }
   
    r->calcNormals();
   
    return r;
}
    
