/*
 * Copyright (C) 2016 Zhang Ge <[email protected]>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zhgeaits.zgdanmaku.utils;

import android.content.res.Resources;
import android.opengl.GLES20;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

/**
 * 加载顶点Shader与片元Shader的工具类
 */
public class ShaderUtils {

    /**
     * 加载制定shader的方法
     *
     * @param shaderType shader的类型  GLES20.GL_VERTEX_SHADER   GLES20.GL_FRAGMENT_SHADER
     * @param source     shader的脚本字符串
     * @return
     */
    public static int loadShader(int shaderType, String source) {
        //创建一个新shader
        int shader = GLES20.glCreateShader(shaderType);

        //若创建成功则加载shader
        if (shader != 0) {

            //加载shader的源代码
            GLES20.glShaderSource(shader, source);

            //编译shader
            GLES20.glCompileShader(shader);

            //存放编译成功shader数量的数组
            int[] compiled = new int[1];

            //获取Shader的编译情况
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);

            //若编译失败则显示错误日志并删除此shader
            if (compiled[0] == 0) {
                ZGLog.e("ES20_ERROR:" + "Could not compile shader " + shaderType + ":");
                ZGLog.e("ES20_ERROR:\n" + GLES20.glGetShaderInfoLog(shader));
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }

        ZGLog.d("loadShader result= " + shader + " shaderType=" + shaderType + ", source=" + source);
        return shader;
    }

    /**
     * 创建shader程序
     *
     * @param vertexSource
     * @param fragmentSource
     * @return 创建成功返回非0值,失败返回0
     */
    public static int createProgram(String vertexSource, String fragmentSource) {

        //加载顶点着色器
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }

        //加载片元着色器
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }

        //创建程序
        int program = GLES20.glCreateProgram();

        //若程序创建成功则向程序中加入顶点着色器与片元着色器
        if (program != 0) {

            //向程序中加入顶点着色器
            GLES20.glAttachShader(program, vertexShader);
            checkGlError("glAttachShader");

            //向程序中加入片元着色器
            GLES20.glAttachShader(program, pixelShader);
            checkGlError("glAttachShader");

            //链接程序
            GLES20.glLinkProgram(program);

            //存放链接成功program数量的数组
            int[] linkStatus = new int[1];

            //获取program的链接情况
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

            //若链接失败则报错并删除程序
            if (linkStatus[0] != GLES20.GL_TRUE) {
                ZGLog.e("ES20_ERROR" + "Could not link program: ");
                ZGLog.e("ES20_ERROR" + GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }
        ZGLog.d("createProgram program=" + program + ", \nvertexSource[" + vertexSource + "]" + ", \nfragmentSource[" + fragmentSource + "]");
        return program;
    }

    /**
     * 检查每一步操作是否有错误的方法
     *
     * @param operation
     */
    public static void checkGlError(String operation) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            ZGLog.e("ES20_ERROR" + operation + ": glError " + error);
            throw new RuntimeException(operation + ": glError " + error);
        }
    }

    /**
     * 从sh脚本中加载shader内容的方法
     *
     * @param fname
     * @param resources
     * @return
     */
    public static String loadFromAssetsFile(String fname, Resources resources) {
        String result = "";
        try {
            InputStream in = resources.getAssets().open(fname);
            int ch;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while ((ch = in.read()) != -1) {
                baos.write(ch);
            }
            byte[] buff = baos.toByteArray();
            baos.close();
            in.close();
            result = new String(buff, "UTF-8");
            result = result.replaceAll("\\r\\n", "\n");

            ZGLog.d("loadFromAssetsFile result=" + result);
        } catch (Exception e) {
            ZGLog.e("loadFromAssetsFile", e);
        }
        return result;
    }
}