// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chromoting.cardboard;

import static org.chromium.chromoting.cardboard.CardboardUtil.makeFloatBuffer;
import static org.chromium.chromoting.cardboard.CardboardUtil.makeRectangularTextureBuffer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.graphics.RectF;
import android.opengl.GLES20;

import org.chromium.chromoting.cardboard.MenuBar.MenuItemType;

import java.nio.FloatBuffer;

/**
 * Cardboard activity menu item representing a corresponding function.
 */
public class MenuItem {
    private static final String VERTEX_SHADER =
            "uniform mat4 u_CombinedMatrix;"
            + "attribute vec4 a_Position;"
            + "attribute vec2 a_TexCoordinate;"
            + "varying vec2 v_TexCoordinate;"
            + "attribute float a_selected;"
            + "varying float v_selected;"
            + "void main() {"
            + "  v_selected = a_selected;"
            + "  v_TexCoordinate = a_TexCoordinate;"
            + "  gl_Position = u_CombinedMatrix * a_Position;"
            + "}";

    private static final String FRAGMENT_SHADER =
            "precision mediump float;"
            + "uniform sampler2D u_Texture;"
            + "varying vec2 v_TexCoordinate;"
            + "varying float v_selected;"
            + "void main() {"
            + "  vec4 texture = texture2D(u_Texture, v_TexCoordinate);"
            + "  if (v_selected > 0.5) {"
            + "    gl_FragColor = vec4(texture.r + 0.5, texture.g + 0.5,"
            + "        texture.b + 0.5, texture.a);"
            + "  } else {"
            + "    gl_FragColor = texture;"
            + "  }"
            + "}";

    private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextureBuffer(
            0.0f, 1.0f, 0.0f, 1.0f);

    private static final int POSITION_COORDINATE_DATA_SIZE = 3;
    private static final int TEXTURE_COORDINATE_DATA_SIZE = 2;

    // Number of vertices passed to glDrawArrays().
    private static final int VERTICES_NUMBER = 6;

    private final FloatBuffer mPositionCoordinates;

    private int mVertexShaderHandle;
    private int mFragmentShaderHandle;
    private int mProgramHandle;
    private int mCombinedMatrixHandle;
    private int mTextureUniformHandle;
    private int mPositionHandle;
    private int mTextureDataHandle;
    private int mTextureCoordinateHandle;
    private int mItemSelectedHandle;

    private MenuItemType mType;

    private RectF mRect;

    public MenuItem(Context context, MenuItemType type, RectF rect) {
        mType = type;
        mRect = new RectF(rect);
        float halfHeight = mRect.height() / 2;
        float halfWidth = mRect.width() / 2;
        mPositionCoordinates = makeFloatBuffer(new float[] {
            // Desktop model coordinates.
            -halfWidth, halfHeight, 0.0f,
            -halfWidth, -halfHeight, 0.0f,
            halfWidth, halfHeight, 0.0f,
            -halfWidth, -halfHeight, 0.0f,
            halfWidth, -halfHeight, 0.0f,
            halfWidth, halfHeight, 0.0f
        });

        mVertexShaderHandle =
                ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
        mFragmentShaderHandle =
                ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
        mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle,
                mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordinate",
                    "a_selected", "u_CombinedMatrix", "u_Texture"});

        mCombinedMatrixHandle =
                GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix");
        mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
        mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
        mItemSelectedHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_selected");
        mTextureDataHandle = TextureHelper.createTextureHandle();
        mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");

        Bitmap texture = BitmapFactory.decodeResource(context.getResources(),
                    type.resourceId());
        TextureHelper.linkTexture(mTextureDataHandle, texture);
        texture.recycle();
    }

    /**
     * Return the type of this menu item.
     */
    public MenuItemType getType() {
        return mType;
    }

    /**
     * Return the position of the center of this menu item.
     */
    public PointF getPosition() {
        return new PointF(mRect.centerX(), mRect.centerY());
    }

    /**
     * Return true if the point is inside this menu item.
     */
    public boolean contains(PointF point) {
        return mRect.contains(point.x, point.y);
    }

    /**
     * Draw menu item according to the given model view projection matrix.
     */
    public void draw(float[] combinedMatrix, boolean selected) {
        GLES20.glUseProgram(mProgramHandle);

        // Pass in model view project matrix.
        GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatrix, 0);

        // Pass in whether the item is selected
        GLES20.glVertexAttrib1f(mItemSelectedHandle, selected ? 1.0f : 0.0f);

        // Pass in model position.
        GLES20.glVertexAttribPointer(mPositionHandle, POSITION_COORDINATE_DATA_SIZE,
                GLES20.GL_FLOAT, false, 0, mPositionCoordinates);
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Pass in texture coordinate.
        GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINATE_DATA_SIZE,
                GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES);
        GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

        // Enable the transparent background.
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
        GLES20.glUniform1i(mTextureUniformHandle, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER);

        GLES20.glDisable(GLES20.GL_BLEND);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
    }

    /*
     * Clean menu item related opengl data.
     */
    public void clean() {
        GLES20.glDeleteShader(mVertexShaderHandle);
        GLES20.glDeleteShader(mFragmentShaderHandle);
        GLES20.glDeleteTextures(1, new int[]{mTextureDataHandle}, 0);
    }
}