package com.example.opengles3;

import android.opengl.GLES30;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;

public class Valjak {
    private FloatBuffer vertexBuffer;//plašt
    float a = 0.5f;
    private int lokacijaMatrice;
    private int lokacijaBoje;
    float vrhovi[];//plašt
    float vrhovi2[];//donja baza
    float vrhovi3[];//gornja baza
    int n;
    private float color[] = new float[] {0.3f, 0.3f, 0.3f, 1};
    private final String vertexShaderCode =
            "#version 300 es\n" +
                    "      in vec4 a_position;\n" +
                    "      in vec3 a_normal;\n" +
                    "      in vec4 v_boja;\n" +
                    "      out float f_boja;\n" +
                    "      uniform mat4 matrica;\n" +
                    "      uniform vec3 izvor;\n" +
                    "      out float svjetlina;"+
                    "      void main() {\n" +
                    "        vec4 vrh = matrica * a_position;"+
                    "        vec3 normala = mat3(matrica) * a_normal;"+

                    "// Lambertov zakon\n" +
                    "        vec3 premaIzvoru = normalize(izvor - vec3(vrh));\n" +
                    "        svjetlina = dot(premaIzvoru, normala);\n" +
                    "\n" +
                    "        // zakon refleksije\n" +
                    "        float refleksija = 0.0;\n" +
                    "        if(svjetlina > 0.0) {\n" +
                    "          vec3 premaKameri = normalize(vec3(0, 5, -12) - vec3(vrh));\n" +
                    "          vec3 reflektiranaZraka = reflect(-premaIzvoru, normala);\n" +
                    "          refleksija = max(dot(reflektiranaZraka, premaKameri), 0.0);\n" +
                    "          refleksija = pow(refleksija, 2.0);\n" +
                    "        }\n" +
                    "\n" +
                    "        svjetlina = svjetlina * 0.7 + refleksija * 0.0+0.3;"+

                    "        f_boja = abs(sin((a_position.x+a_position.z)*10.0f));\n" +
                    "        gl_Position = vrh; // primijeni matricu transformacije\n" +
                    "      }";
    private final String fragmentShaderCode =
            "#version 300 es\n" +
                    "      precision mediump float;\n" +
                    "      in float f_boja;\n" +
                    "      in float svjetlina;"+
                    "      out vec4 bojaPiksela;\n" +
                    "\n" +
                    "      void main() {\n" +
                    "       float x = f_boja;"+
                    "        bojaPiksela = vec4(0.0f,0.0f,x*svjetlina, 1);\n" +
                    "      }";
    private int shaderProgram;
    public static int loadShader(int type, String shaderCode){
        int shader = GLES30.glCreateShader(type);
        GLES30.glShaderSource(shader, shaderCode);
        GLES30.glCompileShader(shader);
        return shader;
    }

    public Valjak(float radius, float visina, int _n){
        n=_n;
        ArrayList<Float> pos=new ArrayList<>();
        double phi;
        // n-terokut - donja baza valjka
        phi = 2 * Math.PI / n;
        Float temp[]={0f, 0f, 0f, 0f, -1f, 0f};
        pos.addAll(Arrays.asList(temp)); // srediste za TRIANGLE_FAN
        for(int i = 0; i <= n; i++) {
            temp= new Float[]{radius * (float)Math.cos(phi),  0f,radius * (float)Math.sin(phi),
                     0f, -1f, 0f};
            pos.addAll(Arrays.asList(temp));
            phi += 2 * Math.PI / n;
        }

        // n-terokut - gornja baza valjka
        phi = 2 * Math.PI;
        temp= new Float[]{0f, visina, 0f, 0f, 1f, 0f};
        pos.addAll(Arrays.asList(temp)); // srediste za TRIANGLE_FAN
        for(int i = 0; i <= n; i++) {
            temp= new Float[]{radius * (float)Math.cos(phi), visina, radius * (float)Math.sin(phi),
                     0f, 1f, 0f};
            pos.addAll(Arrays.asList(temp));
            phi -= 2 * Math.PI / n;
        }

        // plast valjka
        phi = 0;
        for(int i = 0; i <= n; i++) {
            float c = (float)Math.cos(phi);
            float s = (float)Math.sin(phi);
            float x = radius * c;
            float z = radius * s;
            temp= new Float[]{x, 0f, z, c, 0f, s};
            pos.addAll(Arrays.asList(temp));
            temp= new Float[]{x,visina, z,   c, 0f, s};
            pos.addAll(Arrays.asList(temp));
            phi += 2 * Math.PI / n;
        }

        vrhovi = new float[pos.size()];    //All vertices
        for (int i=0;i<vrhovi.length;i++){
            vrhovi[i]=pos.get(i);
        }



        ByteBuffer bb = ByteBuffer.allocateDirect(vrhovi.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(vrhovi);
        vertexBuffer.position(0);
        int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode);
        shaderProgram = GLES30.glCreateProgram();
        GLES30.glAttachShader(shaderProgram, vertexShader);
        GLES30.glAttachShader(shaderProgram, fragmentShader);

        // Bind vPosition to attribute 0
        GLES30.glBindAttribLocation(shaderProgram, 0, "a_position");
        GLES30.glLinkProgram(shaderProgram);

        int[] linkStatus = new int[1];
        GLES30.glGetProgramiv(shaderProgram, GLES30.GL_LINK_STATUS, linkStatus, 0);
        if (linkStatus[0] != GLES30.GL_TRUE) {
            GLES30.glDeleteProgram(shaderProgram);
            throw new RuntimeException("Could not link shader program: "
                    + GLES30.glGetProgramInfoLog(shaderProgram));
        }
    }

    public void draw(float[] matrica) {

        GLES30.glUseProgram(shaderProgram);
        lokacijaMatrice = GLES30.glGetUniformLocation(shaderProgram, "matrica");
        lokacijaBoje = GLES30.glGetAttribLocation(shaderProgram, "v_boja");
        int lokacijaIzvora = GLES30.glGetUniformLocation(shaderProgram, "izvor");

        GLES30.glUniformMatrix4fv(lokacijaMatrice, 1, false, matrica, 0);



        GLES30.glUniform3f(lokacijaIzvora,-10, 6, -10);


        int VERTEX_POS_INDX = 0;
        vertexBuffer.position(VERTEX_POS_INDX);
        int VERTEX_NORMAL_INDX = GLES30.glGetAttribLocation(shaderProgram, "a_normal");


        //CRTANJE VALJKA
        GLES30.glVertexAttribPointer(VERTEX_POS_INDX, 3, GLES30.GL_FLOAT,
                false, 24, vertexBuffer);
        GLES30.glVertexAttribPointer(VERTEX_NORMAL_INDX, 3, GLES30.GL_FLOAT,
                false, 24, vertexBuffer);
        //GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER,0);
        //GLES30.glVertexAttribPointer(VERTEX_POS_INDX, 3, GLES30.GL_FLOAT, false, 24, 0);
        //GLES30.glVertexAttribPointer(VERTEX_NORMAL_INDX, 3, GLES30.GL_FLOAT, false, 24, 12);
        GLES30.glEnableVertexAttribArray(VERTEX_POS_INDX);
        GLES30.glEnableVertexAttribArray(VERTEX_NORMAL_INDX);

        GLES30.glVertexAttrib3fv(lokacijaBoje, color, 0);
        //GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, vrhovi.length/3);
        // donja baza valjka
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, 0, n + 2);

        // gornja baza valjka
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_FAN, n + 2, n + 2);

        // plast valjka
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 2 * (n + 2), 2 * n + 2);
    }
}
