package com.asha.vrlib.common;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

import com.asha.vrlib.objects.MDAbsObject3D;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

/**
 * Created by nitro888 on 15. 4. 5..
 *
 * modify by hzqiujiadi on 16/1/22.
 * hzqiujiadi ashqalcn@gmail.com
 */
public class GLUtil {

    private static final String TAG = "GLUtil";

    public static final float[] sIdentityMatrix = new float[16];

    static {
        Matrix.setIdentityM(sIdentityMatrix, 0);
    }

    public static float[] identityMatrix() {
        return sIdentityMatrix;
    }

    /**
     * Check if the system supports OpenGL ES 2.0.
     *
     * @param context
     * @return true:supported
     */
    public static boolean supportsEs2(Context context){
        final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
        return configurationInfo.reqGlEsVersion >= 0x20000;
    }

    public static void glCheck(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e(TAG, op + ": glError " + android.opengl.GLUtils.getEGLErrorString(error));
        }
    }

    public static String readTextFileFromRaw(final Context context, final int resourceId) {
        final InputStream inputStream = context.getResources().openRawResource(
                resourceId);
        final InputStreamReader inputStreamReader = new InputStreamReader(
                inputStream);
        final BufferedReader bufferedReader = new BufferedReader(
                inputStreamReader);

        String nextLine;
        final StringBuilder body = new StringBuilder();

        try {
            while ((nextLine = bufferedReader.readLine()) != null) {
                body.append(nextLine);
                body.append('\n');
            }
        } catch (IOException e) {
            return null;
        }

        return body.toString();
    }

    /**
     * Helper function to compile a shader.
     *
     * @param shaderType   The shader type.
     * @param shaderSource The shader source code.
     * @return An OpenGL handle to the shader.
     */
    public static int compileShader(final int shaderType, final String shaderSource) {
        int shaderHandle = GLES20.glCreateShader(shaderType);

        if (shaderHandle != 0) {
            // Pass in the shader source.
            GLES20.glShaderSource(shaderHandle, shaderSource);

            // Compile the shader.
            GLES20.glCompileShader(shaderHandle);

            // Get the compilation status.
            final int[] compileStatus = new int[1];
            GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

            // If the compilation failed, delete the shader.
            if (compileStatus[0] == 0) {
                Log.e(TAG, "Error compiling shader: " + GLES20.glGetShaderInfoLog(shaderHandle));
                GLES20.glDeleteShader(shaderHandle);
                shaderHandle = 0;
            }
        }

        if (shaderHandle == 0) {
            throw new RuntimeException("Error creating shader.");
        }

        return shaderHandle;
    }

    /**
     * Helper function to compile and link a program.
     *
     * @param vertexShaderHandle   An OpenGL handle to an already-compiled vertex shader.
     * @param fragmentShaderHandle An OpenGL handle to an already-compiled fragment shader.
     * @param attributes           Attributes that need to be bound to the program.
     * @return An OpenGL handle to the program.
     */
    public static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) {
        int programHandle = GLES20.glCreateProgram();

        if (programHandle != 0) {
            // Bind the vertex shader to the program.
            GLES20.glAttachShader(programHandle, vertexShaderHandle);

            // Bind the fragment shader to the program.
            GLES20.glAttachShader(programHandle, fragmentShaderHandle);

            // Bind attributes
            if (attributes != null) {
                final int size = attributes.length;
                for (int i = 0; i < size; i++) {
                    GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
                }
            }

            // Link the two shaders together into a program.
            GLES20.glLinkProgram(programHandle);

            // Get the link status.
            final int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);

            // If the link failed, delete the program.
            if (linkStatus[0] == 0) {
                Log.e(TAG, "Error compiling program: " + GLES20.glGetProgramInfoLog(programHandle));
                GLES20.glDeleteProgram(programHandle);
                programHandle = 0;
            }
        }

        if (programHandle == 0) {
            throw new RuntimeException("Error creating program.");
        }

        return programHandle;
    }

    public static void loadObject3D(final Context context, final int resourceId, final MDAbsObject3D output) {
        ArrayList<String> vertexes  = new ArrayList<String>();
        ArrayList<String> textures  = new ArrayList<String>();
        ArrayList<String> faces     = new ArrayList<String>();

        InputStream iStream         = context.getResources().openRawResource(resourceId);
        InputStreamReader isr       = new InputStreamReader(iStream);
        BufferedReader bReader      = new BufferedReader(isr);
        String line;
        try {
            while ((line = bReader.readLine()) != null) {
                if (line.startsWith("v "))  vertexes.add(line.substring(2));
                if (line.startsWith("vt ")) textures.add(line.substring(3));
                if (line.startsWith("f "))  faces.add(line.substring(2));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        final float[] vertexBuffer  = new float[faces.size() * 3 * 3];
        final float[] textureBuffer = new float[faces.size() * 3 * 2];
        final short[] indexBuffer   = new short[faces.size() * 3];

        int vertexIndex = 0;
        int textureIndex= 0;
        // int normalIndex = 0;
        int faceIndex   = 0;

        for (String i : faces) {
            for (String j : i.split(" ")) {
                indexBuffer[faceIndex] = (short)faceIndex++;
                String[] faceComponent = j.split("/");

                // only support f v/t/n mode
                String vertex   = vertexes.get(Integer.parseInt(faceComponent[0]) - 1);
                String texture  = textures.get(Integer.parseInt(faceComponent[1]) - 1);
                //String normal   = normals.get(Integer.parseInt(faceComponent[2]) - 1);

                String vertexComp[]     = vertex.split(" ");
                String textureComp[]    = texture.split(" ");
                //String normalComp[]     = normal.split(" ");

                for (String v : vertexComp)     vertexBuffer[vertexIndex++]= Float.parseFloat(v);
                for (String t : textureComp)    textureBuffer[textureIndex++]  = Float.parseFloat(t);
                //for (String n : normalComp)     normalBuffer[normalIndex++]= Float.parseFloat(n);
            }
        }

        // Vertex
        FloatBuffer vertex = ByteBuffer.allocateDirect(vertexBuffer.length * 4)
                .order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexBuffer);
        vertex.position(0);

        // Texture Coordinate
        FloatBuffer texture = ByteBuffer.allocateDirect(textureBuffer.length * 4).
                order(ByteOrder.nativeOrder()).asFloatBuffer().put(textureBuffer);
        texture.position(0);

        output.setVerticesBuffer(0,vertex);
        output.setVerticesBuffer(1,vertex);
        output.setTexCoordinateBuffer(0,texture);
        output.setTexCoordinateBuffer(1,texture);
        output.setNumIndices(indexBuffer.length);

        //ByteBuffer.allocateDirect(normalBuffer.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(normalBuffer).position(0);
    }

}
