#include "ObjFile.h"

#include "Model3D.h"
#include "Vec2.h"

using namespace Framework;

ObjFile::ObjFile(const char* path)
    : ReferenceCounter()
{
    file.setFile(path);
    file.open(File::Style::read);
    Text* line = file.readLine();
    while (line)
    {
        if (line->positionOf("o ") == 0) objectNames.add(line->getTeilText(2));
        line->release();
        line = file.readLine();
    }
    file.close();
}

Vec3<int> ObjFile::parseIndex(Text* s)
{
    Text* x = s->getTeilText(0, s->positionOf("/"));
    Text* y = s->getTeilText(s->positionOf("/") + 1, s->positionOf("/", 1));
    Text* z = s->getTeilText(s->positionOf("/", 1) + 1);
    Vec3<int> result((int)*x, (int)*y, (int)*z);
    x->release();
    y->release();
    z->release();
    s->release();
    return result;
}

void ObjFile::readModel(Model3DData* zTarget)
{
    Array<Vec3<float>> vertices;
    Array<Vec2<float>> textureCoordinates;
    Array<Vec3<float>> normals;
    Array<Vec3<int>> indices;
    // read model info
    Text* line = file.readLine();
    while (line)
    {
        if (line->positionOf("o ") == 0)
        {
            line->release();
            break;
            ;
        }
        if (line->positionOf("v ") == 0)
        {
            Text* x = line->getTeilText(2, line->positionOf(" ", 1));
            Text* y = line->getTeilText(
                line->positionOf(" ", 1) + 1, line->positionOf(" ", 2));
            Text* z = line->getTeilText(line->positionOf(" ", 2) + 1);
            vertices.add(Vec3<float>((float)*x, (float)*y, (float)*z));
            x->release();
            y->release();
            z->release();
        }
        if (line->positionOf("vt ") == 0)
        {
            Text* x = line->getTeilText(3, line->positionOf(" ", 1));
            Text* y = line->getTeilText(line->positionOf(" ", 1) + 1);
            textureCoordinates.add(Vec2<float>((float)*x, (float)*y));
            x->release();
            y->release();
        }
        if (line->positionOf("vn ") == 0)
        {
            Text* x = line->getTeilText(3, line->positionOf(" ", 1));
            Text* y = line->getTeilText(
                line->positionOf(" ", 1) + 1, line->positionOf(" ", 2));
            Text* z = line->getTeilText(line->positionOf(" ", 2) + 1);
            normals.add(Vec3<float>((float)*x, (float)*y, (float)*z));
            x->release();
            y->release();
            z->release();
        }
        if (line->positionOf("f ") == 0)
        {
            // only triangulated meshes ares supported
            Text* p1 = line->getTeilText(2, line->positionOf(" ", 1));
            Text* p2 = line->getTeilText(
                line->positionOf(" ", 1) + 1, line->positionOf(" ", 2));
            Text* p3 = line->getTeilText(line->positionOf(" ", 2) + 1);
            indices.add(parseIndex(p1));
            indices.add(parseIndex(p2));
            indices.add(parseIndex(p3));
        }
        line->release();
        line = file.readLine();
    }
    // compute vertex data and index buffer
    Array<Vertex3D> modelData;
    int* indexList = new int[indices.getEntryCount()];
    int rInd = 0;
    for (Vec3<int> index : indices)
    {
        Vertex3D current;
        current.id = 0;
        current.pos = vertices.get(index.x - 1);
        current.tPos = textureCoordinates.get(index.y - 1);
        current.normal = normals.get(index.z - 1);
        current.knochenId = 0;
        int ind = 0;
        for (const Vertex3D& other : modelData)
        {
            if (other.pos == current.pos && other.tPos == current.tPos
                && other.normal == current.normal)
                break;
            ind++;
        }
        if (modelData.getEntryCount() == ind) modelData.add(current);
        indexList[rInd++] = ind;
    }
    Vertex3D* vertexData = new Vertex3D[modelData.getEntryCount()];
    int ind = 0;
    for (const Vertex3D& vertex : modelData)
        vertexData[ind++] = vertex;
    zTarget->setVertecies(vertexData, modelData.getEntryCount());
    Polygon3D* polygon = new Polygon3D();
    polygon->indexAnz = indices.getEntryCount();
    polygon->indexList = indexList;
    zTarget->addPolygon(polygon);
}

bool ObjFile::loadModel(const char* name, Model3DData* zTarget)
{
    file.open(File::Style::read);
    Text* line = file.readLine();
    while (line)
    {
        if (line->positionOf("o ") == 0 && line->positionOf(name) == 2
            && line->getLength() == textLength(name) + 3)
        {
            readModel(zTarget);
            line->release();
            file.close();
            return 1;
        }
        line->release();
        line = file.readLine();
    }
    file.close();
    return 0;
}

const RCArray<Text>& ObjFile::getContainedModelNames() const
{
    return objectNames;
}