package jomodel;


import java.io.*;
import java.util.*;


/*
 * Read an OBJ file and load it into a JOMesh.  JOMesh is a basic
 * mesh object that holds vertex and triangle data.  The JOOBJReader
 * loads data pretty much as it is in the OBJ, and this Importer converts
 * the idiosyncracies of the OBJ format into a straightforward vert/triangle
 * structure.
 *
 * NOTE: OBJ files can contain polygon faces. The JOMesh holds only
 * triangles, so all faces will be converted to triangles.
 *
 * jun 2006: in makeMeshObject() store uv coord with each vertex.
 * jul 2006: preserve face groups from the obj.
 */
public class JOOBJImporter {

    private JOOBJReader reader = null;
    private JOMesh mesh = null;

    public JOOBJImporter() {
    }

    public JOMesh load(String filename) {
        System.out.println("JOOBJImporter.import(): Load object from OBJ " + filename);
        reader = new JOOBJReader(filename);
        System.out.println("JOOBJImporter.importFromStream(): model has " + reader.faces.size() + " faces and " + reader.vertices.size() + " vertices.  Mtl file is " + reader.materialLibeName);
        return makeMeshObject(reader);
    }

    public JOMesh importFromStream(InputStream inStream) {
        System.out.println("JOOBJImporter.importFromStream(): Load object from OBJ...");
        reader = new JOOBJReader(inStream);
        System.out.println("JOOBJImporter.importFromStream(): model has " + reader.faces.size() + " faces and " + reader.vertices.size() + " vertices.  Mtl file is " + reader.materialLibeName);
        return makeMeshObject(reader);
    }

    /**
     * create a GL_Mesh (mesh object) from the data read by a GL_OBJ_Reader
     */
    public JOMesh makeMeshObject(JOOBJReader objData) {
        ArrayList<float[]> verts = objData.vertices;
        ArrayList<float[]> txtrs = objData.textureCoords;
        ArrayList<float[]> norms = objData.normals;
        ArrayList<JOFace>  faces = objData.faces;

        // make a new mesh
        mesh = new JOMesh();
        mesh.name = objData.filename;
        mesh.materialLibeName = objData.materialLibeName;
        mesh.materials = (objData.materialLib != null)? objData.materialLib.materials : null;

        // add verts to GL_Mesh
        for (int i = 0; i < verts.size(); i++) {
            float[] coords = verts.get(i);
            mesh.addVertex(coords[0], coords[1], coords[2]);
        }

        // allocate space for groups
        mesh.makeGroups(objData.numGroups());

        // init each group (allocate space for triangles)
        for (int g = 0; g < objData.numGroups(); g++) {
            mesh.initGroup(g,
                    objData.getGroupName(g),
                    objData.getGroupMaterialName(g),
                    objData.getGroupTriangleCount(g));
        }

        // add triangles to GL_Mesh.  OBJ "face" may be a triangle,
        // quad or polygon.  Convert all faces to triangles.
        for (int g = 0; g < objData.numGroups(); g++) {
            int triCount=0;
            faces = objData.getGroupFaces(g);
            for (int i = 0; i < faces.size(); i++) {
                JOFace face = faces.get(i);
                // put verts, normals, texture coords into triangle(s)
                if (face.vertexIDs.length == 3) {
                    addTriangle(mesh, g, triCount, face, txtrs, norms, 0, 1, 2, face.materialID);
                    triCount++;
                }
                else if (face.vertexIDs.length == 4) {
                    // convert quad to two triangles
                    addTriangle(mesh, g, triCount, face, txtrs, norms, 0, 1, 2, face.materialID);
                    triCount++;
                    addTriangle(mesh, g, triCount, face, txtrs, norms, 0, 2, 3, face.materialID);
                    triCount++;
                }
                else {
                    // convert polygon to triangle fan, with first vertex (0)
                    // at center:  0,1,2   0,2,3   0,3,4   0,4,5
                    for (int n = 0; n < face.vertexIDs.length - 2; n++) {
                        addTriangle(mesh, g, triCount, face, txtrs, norms, 0, n + 1, n + 2, face.materialID);
                        triCount++;
                    }
                }
            }
        }

        // optimize the GL_Mesh
        mesh.rebuild();

        // if no normals were loaded, generate some
        if (norms.size() == 0) {
            mesh.regenerateNormals();
        }

        return mesh;
    }

    /**
     * Add a new triangle to the GL_Mesh.  This assumes that the
     * vertices have already been added to the GL_Mesh, in the same
     * order that they were in the OBJ.  Also the mesh has groups allocated
     * with triangle arrays.
     *
     * @param obj       GL_Mesh
     * @param groupNum  the group to add the triangles to
     * @param triNum    the index of the triangle in the group
     * @param face      a face from the OBJ file
     * @param txtrs     ArrayList of texture coords from the OBJ file
     * @param norms     ArrayList of normals from the OBJ file
     * @param v1        vertices to use for the triangle (face may have >3 verts)
     * @param v2
     * @param v3
     * @return
     */
    public JOTriangle addTriangle(JOMesh obj, int groupNum, int triNum, JOFace face,
            ArrayList<float[]> txtrs, ArrayList<float[]> norms,
            int v1, int v2, int v3, int mtlID) {
        // An OBJ face may have many vertices (can be a polygon).
        // Make a new triangle with the specified three verts.
        JOTriangle t = new JOTriangle(
                obj.vertex(face.vertexIDs[v1]),
                obj.vertex(face.vertexIDs[v2]),
                obj.vertex(face.vertexIDs[v3]));

        // put texture coords into triangle
        if (txtrs.size() > 0) { // if texture coords were loaded
            float[] uvw;
            uvw = txtrs.get(face.textureIDs[v1]); // txtr coord for vert 1
            t.uvw1 = new JOVector(uvw[0], uvw[1], uvw[2]);
            uvw = txtrs.get(face.textureIDs[v2]); // txtr coord for vert 2
            t.uvw2 = new JOVector(uvw[0], uvw[1], uvw[2]);
            uvw = txtrs.get(face.textureIDs[v3]); // txtr coord for vert 3
            t.uvw3 = new JOVector(uvw[0], uvw[1], uvw[2]);
        }

        // put normals into triangle (NOTE: normalID can be -1!!! could barf here!!!)
        if (norms.size() > 0) { // if normals were loaded
            float[] norm;
            norm = norms.get(face.normalIDs[v1]); // normal for vert 1
            t.norm1 = new JOVector(norm[0], norm[1], norm[2]);
            norm = norms.get(face.normalIDs[v2]); // normal for vert 2
            t.norm2 = new JOVector(norm[0], norm[1], norm[2]);
            norm = norms.get(face.normalIDs[v3]); // normal for vert 3
            t.norm3 = new JOVector(norm[0], norm[1], norm[2]);
        }

        // store material number in triangle
        t.materialID = mtlID;

        // add triangle to given group in GL_Mesh
        obj.addTriangle(t, groupNum, triNum);

        return t;
    }
}