#include <WFX/scene/SceneGraphSerializer.h>
#include <WFX/lang/WFXSystem.h>
#include <WFX/scene/MaterialManager.h>

using namespace WFX;
using namespace WFX::scene;
using namespace WFX::io;


SceneGraphSerializer::SceneGraphSerializer(WFX::io::OutputStream* o) : Output(o) {
    //write magic number
    *Output << SGC_MAGICNUMBER;
    //write version
    *Output << SGC_VERSION;
    //write materials
    writeMaterials();
}

void SceneGraphSerializer::visitNode(Node& n) {
    n.updateDown();
    *Output << SGC_NODE; 
    //write spatial data
    writeSpatialData(n);
    //number of Children
    UInt num = n.size();
    *Output << num;
}

void SceneGraphSerializer::visitTriMesh(TriMesh& t) {
    *Output << SGC_TRIMESH;
    //write spatial data
    writeSpatialData(t);
    //write geomnode data
    writeGeomNodeData(t); 
    //facecount
    UInt fc = t.getFaceCount();
    *Output << fc;
    //connectivity
    UInt* conn = t.getConnectivity();
    for (UInt i = 0; i < fc; i++) {
        *Output << conn[i*3];
        *Output << conn[i*3+1];
        *Output << conn[i*3+2];
    }
}

void SceneGraphSerializer::visitOctreeNode(OctreeNode& o) {
    *Output << SGC_OCTREENODE;
    //write node id
    *Output << o.NodeID;
    //write spatial data
    writeSpatialData(o);
    //write bounding cube
    visitCube(*(o.BoundingCube));
    //write geometry
    UInt num = o.Geometry.size();
    *Output << num;
    std::list<Spatial*>::iterator it;
    for (it = o.Geometry.begin(); it!=o.Geometry.end(); it++) {
        (*it)->accept(*this);
    }
    //number of children
    *Output << o.childCount();
}

void SceneGraphSerializer::visitSphere(WFX::math::Sphere& s) {
    *Output << SGC_SPHERE;
    write(s.getCenter());
    *Output << s.getRadius();
}

void SceneGraphSerializer::visitCube(WFX::math::Cube& c) {
    *Output << SGC_CUBE;
    write(c.getCenter());
    *Output << c.getWidth();
}

void SceneGraphSerializer::writeMaterials() {
    *Output << SGC_MATERIALS;
    MaterialManager* mm = MaterialManager::getInstancePtr();
    TextureManager* tm = SYS.getTextureManager();
    UInt cnt = mm->Materials.size();
    *Output << cnt;
    //iterate over materials
    std::map<std::string, Material*>::iterator it;
    for (it = mm->Materials.begin(); it != mm->Materials.end(); it++) {
        *Output << (*it).second->getName();
        write((*it).second->getEmissive());
        write((*it).second->getAmbient());
        write((*it).second->getDiffuse());
        write((*it).second->getSpecular());
        *Output << (*it).second->getShininess();
        *Output << (*it).second->getAlpha();
        if ((*it).second->getTexture())
            *Output << tm->Textures[(*it).second->getTexture()].getName();
        else
            *Output << "";
    }
}

void SceneGraphSerializer::writeSpatialData(Spatial& s) {
    *Output << SGC_SPATIALDATA;
    //local transform data
    write(s.LocalRotation);
    write(s.LocalTranslation);
    *Output << s.LocalScale;
    //world transform data
    write(s.WorldRotation);
    write(s.WorldTranslation);
    *Output << s.WorldScale;
    //modelbound
    s.ModelBound->accept(this);
}

void SceneGraphSerializer::writeGeomNodeData(GeometryNode& n) {
    *Output << SGC_GEOMNODEDATA;
    //Vertex count
    *Output << n.getVertexCount();
    //data
    if (n.getVertices()) {
        *Output << SGC_VERTICES;
        write(n.getVertices(), n.getVertexCount());
    }
    if (n.getNormals()) {
        *Output << SGC_NORMALS;
        write(n.getNormals(), n.getVertexCount());
    }
    if (n.getColors()) {
        *Output << SGC_COLORS;
        write(n.getColors(), n.getVertexCount());
    }
    if (n.getTexCoords()) {
        *Output << SGC_TEXCOORDS;
        write(n.getTexCoords(), n.getVertexCount());
    }
    //Material
    if (n.getMaterial()) {
        *Output << SGC_MATERIAL;
        *Output << n.getMaterial()->getName();
    }
    *Output << SGC_GEOMNODEEND; 
}

void SceneGraphSerializer::write(const WFX::math::Matrix3& m) {
    for (UInt x=0; x<3; x++) 
        for (UInt y=0; y<3; y++)
            *Output << m[x][y];            
}

void SceneGraphSerializer::write(const WFX::math::Vector3& v) {
    *Output << v.X;
    *Output << v.Y;
    *Output << v.Z;
}

void SceneGraphSerializer::write(const WFX::math::Vector2& v) {
    *Output << v.X;
    *Output << v.Y;
}

void SceneGraphSerializer::write(const WFX::scene::Color& c) {
    *Output << c.R;
    *Output << c.G;
    *Output << c.B;
    *Output << c.A;
}

void SceneGraphSerializer::write(const WFX::math::Vector3* v, UInt num) {
    //LOG("writing " << num << " vector3s");
    for (UInt i = 0; i < num; i++) {
        write(v[i]);
    }
}

void SceneGraphSerializer::write(const WFX::math::Vector2* v, UInt num) {
    //LOG("writing " << num << " vector2s");
    for (UInt i = 0; i < num; i++) {
        write(v[i]);
    }
}

void SceneGraphSerializer::write(const WFX::scene::Color* c, UInt num) {
    //LOG("writing " << num << " colors");
    for (UInt i = 0; i < num; i++) {
        write(c[i]);
    }
}





