
#include <WFX/scene/WFXLoader.h>

#include <WFX/scene/TriMesh.h>
#include <WFX/scene/TextureManager.h>
#include <WFX/scene/MaterialManager.h>
#include <WFX/io/EndianAwareInputStream.h>
#include <WFX/io/FileInputStream.h>
#include <WFX/scene/SceneGraphSerializer.h>

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

WFXLoader::WFXLoader() {
    Rotate = false;
    Output = new Node();
}

WFXLoader::~WFXLoader() {
    delete Input;
}

Node* WFXLoader::loadFromFile(const WFX::io::File& f, bool r) {
    Rotate = r;
    LOG("Loading WFX File: " << f.toString());
    //open file
    if (!f.isFile())
        THROW("not a File: " << f.toString());

    //input Stream...
    Input = new EndianAwareInputStream(new FileInputStream(f, ios::in | ios::binary));

    try {
        //start parsing
        parse();
    } catch(WFXException& e) {
        RETHROW(e, "Error while parsing file " << f.toString() << " at pos " << Input->getPos());
    }

    //TODO: check whether size() == 1 -> return only child instead
    return Output;
}

void WFXLoader::parse() {
    //check for magic number
    UInt i;
    *Input >> i;
    if (i != SceneGraphSerializer::SGC_MAGICNUMBER)
        THROW("Not the Magic Number! " << i);
    //check Version
    *Input >> i;
    if (i > 1)
        THROW("Unknown version: " << i);
    //read Materials
    readMaterials();
    //read tree
    while (Input->good()) {
        Spatial* tree = parseTree();
        if (tree)
            Output->addChild(tree);
    }
}

Spatial* WFXLoader::parseTree() {
    LOG("parseTree");
    Spatial* ret = NULL;
    UInt i;
    //read type and parse node
    i = 0;
    *Input >> i;
    switch (i) {
        case SceneGraphSerializer::SGC_NODE:
            try {
               ret = parseNode();
            } catch(WFXException& e) {
                RETHROW(e, "error parsing Node");
            }
            break;
        case SceneGraphSerializer::SGC_TRIMESH:
            try {
                ret = parseTriMesh();
            } catch(WFXException& e) {
                RETHROW(e, "error parsing TriMesh");
            }
            break;
        case SceneGraphSerializer::SGC_OCTREENODE:
            try {
                ret = parseOctreeNode();
            } catch(WFXException& e) {
                RETHROW(e, "error parsing OctreeNode");
            }
            break;
        default:
            LOG("End at " << Input->getPos() << " token " << i);
    }

    return ret;
}

Node* WFXLoader::parseNode() {
    LOG("parseNode");
    UInt i;
    *Input >> i;
    Node* ret = new Node();
    switch (i) {
        case SceneGraphSerializer::SGC_SPATIALDATA:
            readSpatialData(ret);
            break;
        default:
            THROW("Unknown Node data type: " << i);
    }
    *Input >> i;
    //read children
    for (UInt n=0; n<i; n++) {
        ret->addChild(parseTree());
    }
    return ret;
}

TriMesh* WFXLoader::parseTriMesh() {
    LOG("parseTriMesh");
    TriMesh* m = new TriMesh();
    UInt i;
    *Input >> i;
    //spatial data
    if (i == SceneGraphSerializer::SGC_SPATIALDATA) 
        readSpatialData(m);
    else
        THROW("Unexpected TriMesh data type: " << i);
    *Input >> i;
    //geometrynode data
    if (i == SceneGraphSerializer::SGC_GEOMNODEDATA) 
        readGeomNodeData(m);
    else
        THROW("Unexpected TriMesh data type: " << i);
        
    //facecount
    UInt fc;
    *Input >> fc;
    m->setFaceCount(fc);
    //connectivity
    UInt* conn = new UInt[fc*3];
    for (UInt i = 0; i < fc; i++) {
        *Input >> conn[i*3];
        *Input >> conn[i*3+1];
        *Input >> conn[i*3+2];
    }
    m->setConnectivity(conn);

    return m;
}

OctreeNode* WFXLoader::parseOctreeNode() {
    LOG("parseOctreeNode");
    UInt i;
    *Input >> i;
    OctreeNode* ret = new OctreeNode((OctreeNode::OctreeNodeID)i);
    //spatial data
    *Input >> i;
    if (i == SceneGraphSerializer::SGC_SPATIALDATA) 
        readSpatialData(ret);
    else
        THROW("Unexpected OctreeNode data type: " << i);
    //bounding cube 
    *Input >> i;
    if (i == SceneGraphSerializer::SGC_CUBE) {
        Vector3 cnt;
        read(cnt);
        Real w;
        *Input >> w;
        ret->setBoundingCube(new Cube(cnt, w));
    } else THROW("Unexpected OctreeNode data type: " << i);
    //geometry
    *Input >> i;
    for (UInt n=0; n<i; n++) {
        ret->addGeometry(parseTree()); 
    }
    //children
    UInt num;
    *Input >> num;
    for (UInt n=0; n<num; n++) {
        *Input >> i;
        if (i != SceneGraphSerializer::SGC_OCTREENODE)
            THROW("unexpected OctreeNode child type: " << i);
        ret->setChild(parseOctreeNode());
    }
    return ret;
}

void WFXLoader::readMaterials() {
    UInt i;
    *Input >> i;
    if (i != SceneGraphSerializer::SGC_MATERIALS)
        THROW("No materialdata present!");
    *Input >> i;
    for (UInt n = 0; n < i; n++) {
        Material* mat = new Material();
        //name
        std::string name;
        *Input >> name;
        mat->setName(name);
        //colors
        Color col;
        read(col);
        mat->setEmissive(col);
        read(col);
        mat->setAmbient(col);
        read(col);
        mat->setDiffuse(col);
        read(col);
        mat->setSpecular(col);
        //reals
        Real r;
        *Input >> r;
        mat->setShininess(r);
        *Input >> r;
        mat->setAlpha(r);
        //texture
        std::string texname;
        *Input >> texname;
        if (texname == "") 
            mat->setTexture(0);
        else
            mat->setTexture(TextureManager::getInstance().regTexture(texname));
        //add
        MaterialManager::getInstance().regMaterial(mat);
    }
}

void WFXLoader::readSpatialData(Spatial* s) {
    //read local translation data
    read(s->LocalRotation);
    read(s->LocalTranslation);
    *Input >> s->LocalScale;
    //read world translation data
    read(s->WorldRotation);
    read(s->WorldTranslation);
    *Input >> s->WorldScale;
    //bounding volumes
    UInt i;
    *Input >> i;
    //modelbound
    switch (i) {
        case SceneGraphSerializer::SGC_SPHERE: {
            Vector3 cnt;
            read(cnt);
            Real rad;
            *Input >> rad;
            s->setModelBound(new Sphere(cnt, rad));
            break; }
        case SceneGraphSerializer::SGC_CUBE: {
            Vector3 cnt;
            read(cnt);
            Real w;
            *Input >> w;
            s->setModelBound(new Cube(cnt, w));
            break; }
        default:
            THROW("unexpected Bounding Volume type: " << i);
    }
}

void WFXLoader::readGeomNodeData(GeometryNode* n) {
    UInt num = 0;
    UInt i = 0;
    //vertexcount
    *Input >> num;
    n->setVertexCount(num);
    //type
    *Input >> i;
    while (i != SceneGraphSerializer::SGC_GEOMNODEEND) {
        switch (i) {
            case SceneGraphSerializer::SGC_VERTICES: {
                Vector3* v = new Vector3[num];
                read(v, num);
                n->setVertices(v);
                break; }
            case SceneGraphSerializer::SGC_NORMALS: {
                Vector3* v = new Vector3[num];
                read(v, num);
                n->setNormals(v);
                break; }
            case SceneGraphSerializer::SGC_TEXCOORDS: {
                Vector2* v = new Vector2[num];
                read(v, num);
                n->setTexCoords(v);
                break; }
            case SceneGraphSerializer::SGC_COLORS: {
                Color* c = new Color[num];
                read(c, num);
                n->setColors(c);
                break; }
            case SceneGraphSerializer::SGC_MATERIAL: {
                string matn;
                *Input >> matn;
                n->setMaterial(MaterialManager::getInstance().getMaterial(matn));
                break; }
            default:
                THROW("unexpected GeometryNodeData type: " << i);
        }
        *Input >> i;
    }
    
}

void WFXLoader::read(WFX::math::Matrix3& m) {
    for (UInt x=0; x<3; x++)
        for (UInt y=0; y<3; y++)
            *Input >> m[x][y];
}

void WFXLoader::read(WFX::math::Vector3& v) {
    *Input >> v.X;
    *Input >> v.Y;
    *Input >> v.Z;
}

void WFXLoader::read(WFX::math::Vector2& v) {
    *Input >> v.X;
    *Input >> v.Y;
}

void WFXLoader::read(WFX::scene::Color& c) {
    *Input >> c.R;
    *Input >> c.G;
    *Input >> c.B;
    *Input >> c.A;
}

void WFXLoader::read(WFX::math::Vector3* v, UInt n) {
    //LOG("reading " << n << " vector3s");
    for (UInt i = 0; i < n; i++) {
        read(v[i]);
    }
}

void WFXLoader::read(WFX::math::Vector2* v, UInt n) {
    //LOG("reading " << n << " vector2s");
    for (UInt i = 0; i < n; i++) {
        read(v[i]);
    }
}

void WFXLoader::read(WFX::scene::Color* c, UInt n) {
    //LOG("reading " << n << " colors");
    for (UInt i = 0; i < n; i++) {
        read(c[i]);
    }
}


