package com.example.wilson.mymediacodecfpvplayer;


import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.EGLExt;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.preference.PreferenceManager;
import android.view.Surface;

import com.google.vrtoolkit.cardboard.PhoneParams;
import com.google.vrtoolkit.cardboard.proto.Phone;
import com.google.vrtoolkit.cardboard.sensors.HeadTracker;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyGLRenderer implements GLSurfaceView.Renderer {
    private HeadTracker mHeadTracker;
    private float[] mRightEyeViewM=new float[16];
    private float[] mLeftEyeViewM=new float[16];
    private float[] mProjM=new float[16];
    private float[] mLeftEyeMVPM=new float[16];
    private float[] mRightEyeMVPM=new float[16];
    private float[] tempEyeViewM=new float[16];
    private float[] mLeftEyeTranslate=new float[16];
    private float[] mRightEyeTranslate=new float[16];
    int[] textures = new int[2];
    //public static boolean next_frame=true;
    //private static final float VIDEO_FORMAT_FOR_OPENGL=16.0f/9.0f;
    SharedPreferences settings;
    private boolean unlimitedOGLFps;
    private boolean swapIntervallZero=true;
    private boolean osd=true;
    private float videoFormat=1.3333f;
    private float modelDistance =3.0f;
    private float videoDistance =5.7f;
    private float interpupilarryDistance=0.2f;
    private float viewportScale=1.0f;
    private boolean headTracking=true;

    private static final int FLOAT_SIZE_BYTES = 4;
    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    private static int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
    private Context mContext;

    private int mProgram;
    private int mTextureID;
    private int maPositionHandle;
    private int maTextureHandle;
    private int maMVPMatrixHandle;
    private FloatBuffer mTriangleVertices;

    private int mDisplay_x,mDisplay_y;
    private int leftViewportWidth;
    private int leftViewPortHeight;
    private int leftViewportX;
    private int leftViewPortY;
    private int rightViewportWidth;
    private int rightViewportHeight;
    private int rightViewportX;
    private int rightViewportY;

    private SurfaceTexture mSurfaceTexture;
    private Surface mDecoderSurface;
    private MyOSDReceiverRenderer mOSD;
    //private boolean updateSurface = false;

    int zaehlerFramerate=0;
    long timeb=0;
    long fps=0;

    private UdpReceiverDecoderThread mDecoder;

    public MyGLRenderer(Context context){
        mContext =context;
        settings= PreferenceManager.getDefaultSharedPreferences(mContext);
        try{
            videoFormat=Float.parseFloat(settings.getString("videoFormat","1.3333"));
        }catch(Exception e){e.printStackTrace();}
        unlimitedOGLFps=settings.getBoolean("unlimitedOGLFps", false);
        osd=settings.getBoolean("osd", false);
        swapIntervallZero=settings.getBoolean("swapIntervallZero",true);
        try{
            videoDistance =Float.parseFloat(settings.getString("videoDistance","5.7"));
        }catch (Exception e){e.printStackTrace();
            videoDistance =5.7f;}
        if(videoDistance >10 || videoDistance <1){
            videoDistance =5.7f;}
        try{
            interpupilarryDistance=Float.parseFloat(settings.getString("interpupillaryDistance","0.2"));
        }catch (Exception e){e.printStackTrace();interpupilarryDistance=0.2f;}
        if(interpupilarryDistance>=1 || interpupilarryDistance<0.0f){interpupilarryDistance=0.2f;}
        headTracking=settings.getBoolean("headTracking", true);
        try{
            modelDistance =Float.parseFloat(settings.getString("modelDistance","3.0"));
        }catch (Exception e){e.printStackTrace();modelDistance =3.0f;}
        if(modelDistance >10 || modelDistance <2){modelDistance =3.0f;}
        try{
            viewportScale =Float.parseFloat(settings.getString("viewportScale","1.0"));
        }catch (Exception e){e.printStackTrace();viewportScale=1.0f;}
        if(viewportScale>1 || viewportScale <=0){viewportScale=1.0f;}

        mTriangleVertices = ByteBuffer.allocateDirect(
                MyGLRendererHelper.getTriangleVerticesDataByFormat(videoFormat, videoDistance).length * FLOAT_SIZE_BYTES)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mTriangleVertices.put(MyGLRendererHelper.getTriangleVerticesDataByFormat(videoFormat, videoDistance)).position(0);
        System.out.println("MyGLRenderer: MyGLRenderer");
    }

    @Override
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
    {
        GLES20.glClearColor(0.0f, 0.0f, 0.07f, 0.0f);
        //
        mProgram = OpenGLHelper.createProgram(MyGLRendererHelper.getVertexShader(), MyGLRendererHelper.getFragmentShader());
        maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        OpenGLHelper.checkGlError("glGetAttribLocation aPosition");
        maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
        OpenGLHelper.checkGlError("glGetAttribLocation aTextureCoord");
        maMVPMatrixHandle=GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
        OpenGLHelper.checkGlError("glGetAttribLocation uMVPMatrix");
        //we have to create the texture for the overdraw,too
        GLES20.glGenTextures(2, textures, 0);
        mTextureID = textures[0];
        //I don't know why,but it seems like when you use both external and normal textures,you have to use normal textures for the first,
        //and the external texture for the second unit; bug ?
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
        OpenGLHelper.checkGlError("glBindTexture mTextureID");
        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_LINEAR);
        //mSurfaceTexture = new SurfaceTexture(mTextureID);
        //Enable double buffering,because MediaCodec and OpenGL don't have any synchronisation ?
        //For me,it seems like db hasn't really implemented or has no effect
        mSurfaceTexture = new SurfaceTexture(mTextureID,false);
        mDecoderSurface=new Surface(mSurfaceTexture);
        mDecoder=new UdpReceiverDecoderThread(mDecoderSurface,5000, mContext);
        mDecoder.startDecoding();
        mOSD=new MyOSDReceiverRenderer(mContext,textures,mLeftEyeViewM,mRightEyeViewM,mProjM,videoFormat, modelDistance, videoDistance);
        mOSD.startReceiving();
        mHeadTracker=HeadTracker.createFromContext(mContext);
        mHeadTracker.setNeckModelEnabled(true);
        final Phone.PhoneParams phoneParams = PhoneParams.readFromExternalStorage();
        if (phoneParams != null) {
            this.mHeadTracker.setGyroBias(phoneParams.gyroBias);
        }
        if (headTracking) {
            mHeadTracker.startTracking();
        }
        if(swapIntervallZero){
            EGL14.eglSwapInterval(EGL14.eglGetCurrentDisplay(), 0);
        }
    }

    @Override
    public void onSurfaceChanged(GL10 glUnused, int width, int height){
        //Setup Matrices
        float ratio = (float) (width / 2) / height;
        Matrix.frustumM(mProjM, 0, -ratio, ratio, -1, 1, 1.0f, 10.0f);
        Matrix.setLookAtM(mLeftEyeViewM, 0, -(interpupilarryDistance / 2), 0.0f, 0.0f, 0.0f, 0.0f, -12, 0.0f, 1.0f, 0.0f);
        Matrix.setLookAtM(mRightEyeViewM, 0, (interpupilarryDistance / 2), 0.0f, 0.0f, 0.0f, 0.0f, - 12, 0.0f, 1.0f, 0.0f);
        Matrix.setIdentityM(mLeftEyeTranslate, 0);
        Matrix.setIdentityM(mRightEyeTranslate, 0);
        Matrix.translateM(mLeftEyeTranslate, 0, (interpupilarryDistance / 2), 0.0f, 0);
        Matrix.translateM(mRightEyeTranslate, 0, -(interpupilarryDistance / 2), 0.0f, 0);
        GLES20.glViewport(0, 0, width, height);
        mDisplay_x=width;
        mDisplay_y=height;
        mOSD.mDisplay_x=width;
        mOSD.mDisplay_y=height;
        leftViewportWidth=(int)((mDisplay_x/2*viewportScale));
        leftViewPortHeight=(int)((mDisplay_y*viewportScale));
        leftViewportX=(int)(((mDisplay_x/2)-leftViewportWidth)/2);
        leftViewPortY=(int)((mDisplay_y-leftViewPortHeight)/2);
        rightViewportWidth=leftViewportWidth;
        rightViewportHeight=leftViewPortHeight;
        rightViewportX=(mDisplay_x/2)+leftViewportX;
        rightViewportY=leftViewPortY;
    }

    @Override
    public void onDrawFrame(GL10 glUnused) {
        //Tell android this frame should has been displayed at the time it was created
        if(unlimitedOGLFps){
            EGLExt.eglPresentationTimeANDROID(EGL14.eglGetCurrentDisplay(),EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW),System.nanoTime());
        }
        if(headTracking && mHeadTracker!=null){
            mHeadTracker.getLastHeadView(tempEyeViewM, 0);
            Matrix.multiplyMM(mLeftEyeViewM, 0,  mLeftEyeTranslate,  0, tempEyeViewM, 0);
            Matrix.multiplyMM(mRightEyeViewM, 0, mRightEyeTranslate, 0, tempEyeViewM, 0);
        }
        Matrix.multiplyMM(mLeftEyeMVPM, 0, mProjM, 0, mLeftEyeViewM, 0);
        Matrix.multiplyMM(mRightEyeMVPM, 0, mProjM, 0, mRightEyeViewM, 0);
        //Danger: getTimestamp can't be used to compare with System.nanoTime or System.currentTimeMillis
        //because it's zero point depends on the sources providing the image;
        mSurfaceTexture.updateTexImage();
        GLES20.glFinish();
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(mProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glVertexAttribPointer(maTextureHandle, 3, GLES20.GL_FLOAT, false,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        //draw left eye
        GLES20.glUniformMatrix4fv(maMVPMatrixHandle, 1, false, mLeftEyeMVPM, 0);

        GLES20.glViewport(leftViewportX, leftViewPortY, leftViewportWidth, leftViewPortHeight);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, MyGLRendererHelper.triangleVerticesDataNumberOfTraingles);
        //draw right eye
        GLES20.glUniformMatrix4fv(maMVPMatrixHandle, 1, false, mRightEyeMVPM, 0);
        GLES20.glViewport(rightViewportX, rightViewportY, rightViewportWidth, rightViewportHeight);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, MyGLRendererHelper.triangleVerticesDataNumberOfTraingles);
        GLES20.glDisableVertexAttribArray(maPositionHandle);
        GLES20.glDisableVertexAttribArray(maTextureHandle);
        GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
        if(osd){
            mOSD.setupModelMatrices();
            GLES20.glViewport(leftViewportX, leftViewPortY, leftViewportWidth, leftViewPortHeight);
            mOSD.drawLeftEye(mLeftEyeViewM);
            GLES20.glViewport(rightViewportX, rightViewportY, rightViewportWidth, rightViewportHeight);
            mOSD.drawRightEye(mRightEyeViewM);
        }
        zaehlerFramerate++;
        if((System.currentTimeMillis()-timeb)>4000) {
            fps = (long)(zaehlerFramerate / 4.0f);
            System.out.println("fps:"+fps);
            if(fps >3 && mDecoder!=null && mOSD!=null){
                mDecoder.tellOpenGLFps(fps);
                mOSD.mDecoder_fps=mDecoder.getDecoderFps();
            }
            timeb = System.currentTimeMillis();
            zaehlerFramerate = 0;
        }

    }
    public void onSurfaceDestroyed() {
        if(mOSD!=null){
            mOSD.stopReceiving();
        }
        if(mDecoder!=null){
            mDecoder.stopDecoding();
        }
        if(mHeadTracker!=null){
            mHeadTracker.stopTracking();
        }
    }


    public void onTap(){
        if(mDecoder!=null){
            mDecoder.next_frame=true;
            /*
            angle_z+=10;
            System.out.println("Angle_z"+angle_z);*/
        }
    }

}