/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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 com.android.systemui.glwallpaper;

import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glUseProgram;

import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
 */
class ImageGLProgram {
    private static final String TAG = ImageGLProgram.class.getSimpleName();

    private Context mContext;
    private int mProgramHandle;

    ImageGLProgram(Context context) {
        mContext = context.getApplicationContext();
    }

    private int loadShaderProgram(int vertexId, int fragmentId) {
        final String vertexSrc = getShaderResource(vertexId);
        final String fragmentSrc = getShaderResource(fragmentId);
        final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
        final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
        return getProgramHandle(vertexHandle, fragmentHandle);
    }

    private String getShaderResource(int shaderId) {
        Resources res = mContext.getResources();
        StringBuilder code = new StringBuilder();

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(res.openRawResource(shaderId)))) {
            String nextLine;
            while ((nextLine = reader.readLine()) != null) {
                code.append(nextLine).append("\n");
            }
        } catch (IOException | Resources.NotFoundException ex) {
            Log.d(TAG, "Can not read the shader source", ex);
            code = null;
        }

        return code == null ? "" : code.toString();
    }

    private int getShaderHandle(int type, String src) {
        final int shader = glCreateShader(type);
        if (shader == 0) {
            Log.d(TAG, "Create shader failed, type=" + type);
            return 0;
        }
        glShaderSource(shader, src);
        glCompileShader(shader);
        return shader;
    }

    private int getProgramHandle(int vertexHandle, int fragmentHandle) {
        final int program = glCreateProgram();
        if (program == 0) {
            Log.d(TAG, "Can not create OpenGL ES program");
            return 0;
        }

        glAttachShader(program, vertexHandle);
        glAttachShader(program, fragmentHandle);
        glLinkProgram(program);
        return program;
    }

    boolean useGLProgram(int vertexResId, int fragmentResId) {
        mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
        glUseProgram(mProgramHandle);
        return true;
    }

    int getAttributeHandle(String name) {
        return glGetAttribLocation(mProgramHandle, name);
    }

    int getUniformHandle(String name) {
        return glGetUniformLocation(mProgramHandle, name);
    }
}
