package org.gearvrf.jassimp2;

import java.nio.CharBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;

import org.gearvrf.GVRContext;
import org.gearvrf.GVRImportSettings;
import org.gearvrf.GVRMesh;
import org.gearvrf.GVRSceneObject;

import org.gearvrf.utility.Log;

public class GVRJassimpAdapter {
    private static final String TAG = GVRJassimpAdapter.class.getSimpleName();
    public static GVRNewWrapperProvider sWrapperProvider = new GVRNewWrapperProvider();
    private static GVRJassimpAdapter sAdapter;

    private GVRJassimpAdapter() {
    }

    public synchronized static GVRJassimpAdapter get() {
        if (sAdapter == null) {
            sAdapter = new GVRJassimpAdapter();
        }
        return sAdapter;
    }

    public static void set(GVRJassimpAdapter newAdapter) {
        sAdapter = newAdapter;
    }

    public GVRMesh createMesh(GVRContext ctx, AiMesh aiMesh) {
        GVRMesh mesh = new GVRMesh(ctx);

        // Vertices
        FloatBuffer verticesBuffer = aiMesh.getPositionBuffer();
        if (verticesBuffer != null) {
            float[] verticesArray = new float[verticesBuffer.capacity()];
            verticesBuffer.get(verticesArray, 0, verticesBuffer.capacity());
            mesh.setVertices(verticesArray);
        }

        // Normals
        FloatBuffer normalsBuffer = aiMesh.getNormalBuffer();
        if (normalsBuffer != null) {
            float[] normalsArray = new float[normalsBuffer.capacity()];
            normalsBuffer.get(normalsArray, 0, normalsBuffer.capacity());
            mesh.setNormals(normalsArray);
        }

        // TexCoords
        final int coordIdx = 0;
        FloatBuffer fbuf = aiMesh.getTexCoordBuffer(coordIdx);
        if (fbuf != null) {
            FloatBuffer coords = FloatBuffer.allocate(aiMesh.getNumVertices() * 2);
            if (aiMesh.getNumUVComponents(coordIdx) == 2) {
                FloatBuffer coordsSource = aiMesh.getTexCoordBuffer(coordIdx);
                coords.put(coordsSource);
            } else {
                for (int i = 0; i < aiMesh.getNumVertices(); ++i) {
                    float u = aiMesh.getTexCoordU(i, coordIdx);
                    float v = aiMesh.getTexCoordV(i, coordIdx);
                    coords.put(u);
                    coords.put(v);
                }
            }
            mesh.setTexCoords(coords.array());
        }

        // Triangles
        IntBuffer indexBuffer = aiMesh.getIndexBuffer();
        if (indexBuffer != null) {
            CharBuffer triangles = CharBuffer.allocate(indexBuffer.capacity());
            for (int i = 0; i < indexBuffer.capacity(); ++i) {
                triangles.put((char)indexBuffer.get());
            }
            mesh.setTriangles(triangles.array());
        }

        return mesh;
    }

    public GVRSceneObject createSceneObject(GVRContext ctx, AiNode node) {
        GVRSceneObject sceneObject = new GVRSceneObject(ctx);
        sceneObject.setName(node.getName());
        return sceneObject;
    }

    public Set<AiPostProcessSteps> toJassimpSettings(EnumSet<GVRImportSettings> settings) {
        Set<AiPostProcessSteps> output = new HashSet<AiPostProcessSteps>();

        for (GVRImportSettings setting : settings) {
            AiPostProcessSteps aiSetting = fromGVRSetting(setting);
            if (aiSetting != null) {
                output.add(aiSetting);
            }
        }

        return output;
    }

    public AiPostProcessSteps fromGVRSetting(GVRImportSettings setting) {
        switch (setting) {
        case CALCULATE_TANGENTS:
            return AiPostProcessSteps.CALC_TANGENT_SPACE;
        case JOIN_IDENTICAL_VERTICES:
            return AiPostProcessSteps.JOIN_IDENTICAL_VERTICES;
        case TRIANGULATE:
            return AiPostProcessSteps.TRIANGULATE;
        case CALCULATE_NORMALS:
            return AiPostProcessSteps.GEN_NORMALS;
        case CALCULATE_SMOOTH_NORMALS:
            return AiPostProcessSteps.GEN_SMOOTH_NORMALS;
        case LIMIT_BONE_WEIGHT:
            return AiPostProcessSteps.LIMIT_BONE_WEIGHTS;
        case IMPROVE_VERTEX_CACHE_LOCALITY:
            return AiPostProcessSteps.IMPROVE_CACHE_LOCALITY;
        case SORTBY_PRIMITIVE_TYPE:
            return AiPostProcessSteps.SORT_BY_PTYPE;
        case OPTIMIZE_MESHES:
            return AiPostProcessSteps.OPTIMIZE_MESHES;
        case OPTIMIZE_GRAPH:
            return AiPostProcessSteps.OPTIMIZE_GRAPH;
        case FLIP_UV:
            return AiPostProcessSteps.FLIP_UVS;
        default:
            // Unsupported setting
            Log.e(TAG, "Unsupported setting %s", setting);
            return null;
        }
    }
}

@@ -100,32 +100,60 @@
 .nio.IntBuffer;%0A
+import java.util.ArrayList;%0A
 import java.util
@@ -184,24 +184,47 @@
 il.HashSet;%0A
+import java.util.List;%0A
 import java.
@@ -642,51 +642,269 @@
 er s
-Adapter;%0A%0A    private GVRJassimpAdapter() %7B
+Instance;%0A    private List%3CINodeFactory%3E mNodeFactories;%0A%0A    public interface INodeFactory %7B%0A        GVRSceneObject createSceneObject(GVRContext ctx, AiNode node);%0A    %7D%0A%0A    private GVRJassimpAdapter() %7B%0A        mNodeFactories = new ArrayList%3CINodeFactory%3E();
 %0A   
@@ -977,23 +977,24 @@
    if (s
-Adapter
+Instance
  == null
@@ -1006,31 +1006,32 @@
            s
-Adapter
+Instance
  = new GVRJa
@@ -1072,23 +1072,24 @@
 return s
-Adapter
+Instance
 ;%0A    %7D%0A
@@ -1104,85 +1104,268 @@
 lic 
-static void set(GVRJassimpAdapter newAdapter) %7B%0A        sAdapter = newAdapter
+void addNodeFactory(INodeFactory factory) %7B%0A        // Insert new factory in front of the list to support overriding%0A        mNodeFactories.add(0, factory);%0A    %7D%0A%0A    public void removeNodeFactory(INodeFactory factory) %7B%0A        mNodeFactories.remove(factory)
 ;%0A  
@@ -3434,16 +3434,266 @@
 Object =
+ null;%0A%0A        for (INodeFactory factory : mNodeFactories) %7B%0A            sceneObject = factory.createSceneObject(ctx, node);%0A            if (sceneObject != null)%0A                return sceneObject;%0A        %7D%0A%0A        // Default%0A        sceneObject =
  new GVR
@@ -3751,24 +3751,25 @@
 getName());%0A
+%0A
         retu

Update interface for GVRJassimpAdapter