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

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

OBJLoader::OBJLoader() {
    Input = NULL;
    CurrentObject = 0;
    CurrentLine = 0;
    Output = new Node();
}

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

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

    return r;
}

Node* OBJLoader::loadFromFile(const WFX::io::File& f) {
    LOG("parsing OBJ File: " << f.toString());
    Input = new ifstream(f.toString().data());
    if (!Input)
        THROW("Unable to open file: " << f.toString());

    parse();

    return Output;
}

void OBJLoader::parse() {
    StartTicks = TimeTask::getInstance().getTicks();
    while (Input->good()) {
        std::string line = getLine();
        //ignore comments and empty lines
        if (line[0] == '#' || line == "")
            continue;

        //LOG("Line: " << line);
        vector<string> tokens = StringUtils::tokenize(line, " ");
        string type = tokens[0];
        if (type == "o") {
            parseObject(line);
        } else if (type == "v") {
            Vertices.push_back(parseVector3(line));
        } else if (type == "vn") {
            Normals.push_back(parseVector3(line));
        } else if (type == "vt") {
            TexCoords.push_back(parseVector2(line));
        } else if (type == "f") {
            parseFace(line);
        } else {
            LOG("Unknown type: " << type);
        }
    }
    LOG("parsed " << Objects.size() << " Objects");
    LOG("   and " << Vertices.size() << " Vertices");
    LOG("   and " << Normals.size() << " Normals");
    LOG("   and " << TexCoords.size() << " TexCoords");
    finalize();
    ULong dt = TimeTask::getInstance().getTicks() - StartTicks;
    LOG("  time: " << dt);
}

void OBJLoader::finalize() {
    //now iterate over Objects and reconstruct them
    for (UInt i=0; i<Objects.size(); i++) {
        TriMesh* m = new TriMesh(); //convert Object to TriMesh
        LOG("Reconstructing Object " << Objects[i]);
        std::map<UInt, UInt> vertInd;
        std::map<UInt, UInt> normInd;
        std::map<UInt, UInt> texcInd;
        std::vector<OBJFace>::iterator it;
        for (it = Faces[i].begin(); it!=Faces[i].end(); it++) {
            //which vertices?
            std::vector<UInt>::iterator vit;
            for (vit = (*it).Verts.begin(); vit!=(*it).Verts.end(); vit++) {
                vertInd[*vit] = 1; 
            }
            //which normals?
            std::vector<UInt>::iterator nit;
            for (nit = (*it).Normals.begin(); nit!=(*it).Normals.end(); nit++) {
                normInd[*nit] = 1;
            }
            //which normals?
            std::vector<UInt>::iterator tit;
            for (tit = (*it).TexCoords.begin(); tit!=(*it).TexCoords.end(); tit++) {
                texcInd[*tit] = 1;
            }
        }
        
        //now copy all necessary Vertices
        m->VertexCount = vertInd.size();
        m->Vertices = new Vector3[m->VertexCount]; //allocate memory

        UInt idx = 0;
        std::map<UInt, UInt>::iterator mit;
        for (mit = vertInd.begin(); mit != vertInd.end(); mit++) {
            UInt id = (*mit).first-1; //.OBJ starts counting at 1... suckers
            m->Vertices[idx] = Vertices[id];
            vertInd[(*mit).first] = idx; //save new index 
            idx++;
        }
        //reconstruct connectivity - iterate over Faces a second time... 
        //there might be a better way to do this...
        vector<UInt> conn; //connectivity
        for (it = Faces[i].begin(); it!=Faces[i].end(); it++) {
            conn.push_back(vertInd[(*it).Verts[0]]);
            conn.push_back(vertInd[(*it).Verts[1]]);
            conn.push_back(vertInd[(*it).Verts[2]]);
            if ((*it).VertexCount == 4) {
                conn.push_back(vertInd[(*it).Verts[0]]);
                conn.push_back(vertInd[(*it).Verts[2]]);
                conn.push_back(vertInd[(*it).Verts[3]]);
            }
        }
        //now copy connectivity
        {
            m->Connectivity = new UInt[conn.size()];
            m->FaceCount = (UInt)(conn.size()/3);
            vector<UInt>::iterator it;
            UInt i = 0;
            for (it = conn.begin(); it!=conn.end(); it++) {
                m->Connectivity[i] = (*it);
                i++;
            }
        }
        m->calcNormals(); 
        Output->addChild(m);
    }
}

void OBJLoader::parseObject(const std::string& l) {
    CurrentObject = Objects.size();
    Objects.push_back(stripType(l));
}

void OBJLoader::parseFace(const std::string& l) {
    try {
        OBJFace f(stripType(l));
        Faces[CurrentObject].push_back(f);
    } catch(WFXException& e) {
        RETHROW(e, "Unable to parse Face on Line " << CurrentLine << ": " << l);
    }
}

WFX::math::Vector3 OBJLoader::parseVector3(const std::string& l) {
    Vector3 ret;
    ret.parseStr(stripType(l));
    return ret;
}

WFX::math::Vector2 OBJLoader::parseVector2(const std::string& l) {
    Vector2 ret;
    ret.parseStr(stripType(l));
    return ret;
}

std::string OBJLoader::getLine() {
    Char linebuf[1024];
    Input->getline(linebuf, 1023);
    std::string line(linebuf);
    CurrentLine++;
    return line;
}

std::string OBJLoader::stripType(const std::string& str) {
    std::string del(" ");
    //skip delimiters at beginning
    string::size_type lastPos = str.find_first_not_of(del, 0);
    //Find first "non-delimiter"
    string::size_type pos = str.find_first_of(del, lastPos);
    //return substring
    return str.substr(pos);
}

OBJFace::OBJFace(const std::string& l) {
    vector<string> tok = StringUtils::tokenize(l, " ");
    VertexCount = tok.size();
    //check VertexCount for face
    if (VertexCount > 4 || VertexCount < 3) 
        THROW("unsupported VertexCount " << VertexCount << " in Face: " << l );

    vector<string>::iterator it;
    for (it = tok.begin(); it!=tok.end(); it++) {
        vector<string> vals = StringUtils::tokenize(*(it), "/");
        
        if (vals.size() < 1)
            THROW("illegal face descriptor: " << *(it));
          
        Verts.push_back(StringUtils::toUInt(vals[0]));
        if (vals.size() >= 2)
            TexCoords.push_back(StringUtils::toUInt(vals[1]));
        if (vals.size() >= 3)
            Normals.push_back(StringUtils::toUInt(vals[2]));
    }
}

void OBJFace::printDebug() {
    LOG("Face: " << VertexCount << " Vertices");
    for (UInt i=0; i<VertexCount; i++) {
        LOG("  -> V " << Verts[i] << " T " << TexCoords[i] << " N " << Normals[i]);
    }
}

