/*
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * 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.introlab.rtabmap;

import java.util.Vector;
import java.util.concurrent.locks.ReentrantLock;

import android.app.Activity;
import android.app.ProgressDialog;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import android.widget.Toast;

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

// Renderer renders graphic content. This includes the point cloud,
// ground grid, camera frustum, camera axis, and trajectory based on the Tango
// device's pose.
public class Renderer implements GLSurfaceView.Renderer {

	private final float[] mtrxProjection = new float[16];
	private final float[] mtrxView = new float[16];
	private final float[] mtrxProjectionAndView = new float[16];

	private TextManager mTextManager = null;
	private float mSurfaceHeight = 0.0f;
	private float mTextColor = 1.0f;
	private int mOffset = 0;
	private ARCoreSharedCamera mCamera = null;
	private DisplayRotationHelper mDisplayRotationHelper = null;
	
	private Vector<TextObject> mTexts;

	private static RTABMapActivity mActivity;
	public Renderer(RTABMapActivity c) {
		mActivity = c;
		mDisplayRotationHelper = new DisplayRotationHelper(/*context=*/ c);
	}

	private ProgressDialog mProgressDialog = null;
	private Toast mToast = null;
	
	private boolean mTextChanged = false;
	private ReentrantLock mTextLock = new ReentrantLock();

	public void setProgressDialog(ProgressDialog progressDialog)
	{
		mProgressDialog = progressDialog;
	}

	public void setToast(Toast toast)
	{
		mToast = toast;
	}
	
	public void setOffset(int offset)
	{
		mOffset = offset;
	}
	
	public void setCamera(ARCoreSharedCamera camera)
	{
		mCamera = camera;
		if(mCamera!=null)
		{
			mDisplayRotationHelper.onDisplayChanged(0);
			mDisplayRotationHelper.updateSessionIfNeeded(mCamera);
		}
	}

	// Render loop of the Gl context.
	public void onDrawFrame(GL10 useGLES20instead) {

		synchronized (this) {
			if(mActivity.nativeApplication != 0)
			{
				int step = 0;
				try
				{
					if(mCamera!=null)
					{
						step=1;
						mCamera.updateGL();
						
					}
					
					step=2;
					
					final int value = RTABMapLib.render(mActivity.nativeApplication);
		
					if(mTextManager!=null)
					{
						step=3;
						if(mTextChanged)
						{
							mTextChanged = false;
							Vector<TextObject> txtcollection = new Vector<TextObject>();
							
							mTextLock.lock();
						    try {
						    	if(mTexts.size() > 0)
						    	{
						    		txtcollection.addAll(mTexts);
						    	}
						    } finally {
						    	mTextLock.unlock();
						    }
						    
							// Prepare the text for rendering
							mTextManager.PrepareDraw(txtcollection);
						}
						
						float[] mvp = new float[16];
						Matrix.translateM(mvp, 0, mtrxProjectionAndView, 0, 0, mOffset, 0);
						mTextManager.Draw(mvp);
					}
					step=4;
					if(value != 0 && mProgressDialog != null && mProgressDialog.isShowing())
					{
						mActivity.runOnUiThread(new Runnable() {
							public void run() {
								if(!RTABMapActivity.DISABLE_LOG) Log.i("RTABMapActivity", "Renderer: dismiss dialog, value received=" + String.valueOf(value));
								mProgressDialog.dismiss();
								mActivity.resetNoTouchTimer();
							}
						});
					}
					if(value==-1)
					{
						mActivity.runOnUiThread(new Runnable() {
							public void run() {
								if(mToast!=null)
								{
									mToast.makeText(mActivity.getApplicationContext(), String.format("Out of Memory!"), Toast.LENGTH_SHORT).show();
								}
							}
						});
					}
					else if(value==-2)
					{
						mActivity.runOnUiThread(new Runnable() {
							public void run() {
								if(mToast!=null)
								{
									mToast.makeText(mActivity.getApplicationContext(), String.format("Rendering Error!"), Toast.LENGTH_SHORT).show();
								}
							}
						});
					}
				}
				catch(final Exception e)
				{
					final int stepF = step;
					mActivity.runOnUiThread(new Runnable() {
						public void run() {
							if(mToast!=null)
							{
								String msg = String.format("Rendering error! (exception=%s) step=%d", e.getMessage(), stepF);
								Log.e("RTABMapActivity", msg);
								mToast.makeText(mActivity.getApplicationContext(), msg, Toast.LENGTH_LONG).show();
							}
						}
		
					});
				}
			}
		}
	}

	// Called when the surface size changes.
	public void onSurfaceChanged(GL10 useGLES20instead, int width, int height) {
		
		if(mActivity.nativeApplication!=0)
		{
			RTABMapLib.setupGraphic(mActivity.nativeApplication, width, height);
		}
		
		mDisplayRotationHelper.onSurfaceChanged(width, height);
		if(mCamera!=null)
		{
			mDisplayRotationHelper.updateSessionIfNeeded(mCamera);
		}
		
		mSurfaceHeight = (float)height;

		// Clear our matrices
		for(int i=0;i<16;i++)
		{
			mtrxProjection[i] = 0.0f;
			mtrxView[i] = 0.0f;
			mtrxProjectionAndView[i] = 0.0f;
		}

		// Setup our screen width and height for normal sprite translation.
		Matrix.orthoM(mtrxProjection, 0, 0f, width, 0.0f, height, 0, 50);

		// Set the camera position (View matrix)
		Matrix.setLookAtM(mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

		// Calculate the projection and view transformation
		Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
	}

	// Called when the surface is created or recreated.
	public void onSurfaceCreated(GL10 useGLES20instead, EGLConfig config) {

		if(mActivity.nativeApplication != 0)
		{
			RTABMapLib.initGlContent(mActivity.nativeApplication);
		}
		
		// Create our text manager
		mTextManager = new TextManager(mActivity);
		mTextManager.setColor(mTextColor);
	}
	
	public void updateTexts(String[] texts)
	{
		if(mTextManager != null && mSurfaceHeight > 0.0f)
		{
			Vector<TextObject> textObjects = new Vector<TextObject>();
			float offset = mSurfaceHeight-mTextManager.getMaxTextHeight();
			if(texts != null)
			{
				for(int i=0;i<texts.length; ++i)
				{
					if(texts[i]!=null && texts[i].length()>0)
					{
						TextObject txt = new TextObject(texts[i], 0, offset);
						textObjects.add(txt);
					}
					offset-=mTextManager.getMaxTextHeight();
				}
			}
	
			mTextLock.lock();
			try {
				mTexts = textObjects;
			} finally {
				mTextLock.unlock();
			}
	
			mTextChanged = true;
		}
	}
	
	public void setTextColor(float color) 
	{
		mTextColor = color;
		if(mTextManager != null)
		{
			mTextManager.setColor(mTextColor);
		}
	}
}
