package pl.umk.fizyka.cubegl;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

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

import android.os.Bundle;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MotionEvent;

import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.opengl.Matrix;

public class MainActivity extends Activity {

	GLSurfaceView glView = null;
	OpenGLRenderer renderer = null;
	
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        
        GLSurfaceView view = new GLSurfaceView(this);
        renderer = new OpenGLRenderer(getApplicationContext());
        view.setRenderer(renderer);
        setContentView(view);
        
        glView = view; //?????
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    @Override
    protected void onPause()
    {
    	super.onPause();
    	glView.onPause();
    }
    
    @Override
    protected void onResume()
    {
    	super.onResume();
    	glView.onResume();
    }    
    
    @Override
    public boolean onKeyDown(int keyCode,KeyEvent event)
    {
    	switch(keyCode)
    	{
    	case KeyEvent.KEYCODE_W:
    	case KeyEvent.KEYCODE_DPAD_UP:
    		renderer.katX -= 1;
    		break;
    	case KeyEvent.KEYCODE_S:
    	case KeyEvent.KEYCODE_DPAD_DOWN:
    		renderer.katX += 1;
    		break;
    	case KeyEvent.KEYCODE_A:
    	case KeyEvent.KEYCODE_DPAD_LEFT:
    		renderer.katY -= 1;
    		break;
    	case KeyEvent.KEYCODE_D:
    	case KeyEvent.KEYCODE_DPAD_RIGHT:
    		renderer.katY += 1;
    		break;
    	}
    	return super.onKeyDown(keyCode, event);
    }
    
    boolean poprzednie = false;
    float poprzednieX=0;
    float poprzednieY=0;
    
    final float skalowanieZmianyKataDotyk = 0.002f;
    float skalowanieX;
    float skalowanieY;
    
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
    	int akcja = event.getAction();
    	switch(akcja)
    	{
    	case MotionEvent.ACTION_DOWN:
    	case MotionEvent.ACTION_UP:
    		poprzednie=false;
    		break;
    	case MotionEvent.ACTION_MOVE:
    		float X = event.getX();
    		float Y = event.getY();
    		if(poprzednie)
    		{
    			float dX=X-poprzednieX;
    			float dY=Y-poprzednieY;
    			renderer.katX+=skalowanieX*dY;
    			renderer.katY+=skalowanieY*dX;
    		}
    		else
    		{
    			poprzednie=true;
    			Display wyswietlacz=getWindowManager().getDefaultDisplay();
    			skalowanieX=wyswietlacz.getWidth()/2.0f*skalowanieZmianyKataDotyk;
    			skalowanieY=wyswietlacz.getHeight()/2.0f*skalowanieZmianyKataDotyk;
    		}
    		poprzednieX=X;
    		poprzednieY=Y;
    		break;
    	}
    	return super.onTouchEvent(event);
    }
}

class OpenGLRenderer implements android.opengl.GLSurfaceView.Renderer, SensorEventListener
{
	Context context;
	
	public OpenGLRenderer(Context context)
	{
		this.context=context;
	}

	public void onSurfaceCreated(GL10 gl, EGLConfig config) 
	{
		gl.glShadeModel(GL10.GL_SMOOTH);
		
		//takie samo, jak domyslne
		gl.glClearDepthf(1.0f);
		gl.glEnable(GL10.GL_DEPTH_TEST);
		gl.glDepthFunc(GL10.GL_LEQUAL);
		gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
		gl.glDisable(GL10.GL_DITHER);
		
		inicjujBuforWerteksow();
		
		oswietlenie(gl);
		
		if(teksturowanie)
		{
			loadTexture(gl, context);
			gl.glEnable(GL10.GL_TEXTURE_2D);		
		}
		else gl.glDisable(GL10.GL_TEXTURE_2D);
		
		//czujniki
		if(sensorManager==null)
		{
			sensorManager=(SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
			orientacja=sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
			sensorManager.registerListener(this, orientacja, SensorManager.SENSOR_DELAY_GAME);
		}
	}

	private void ustawieniaSceny(GL10 gl, int szer, int wys)
	{
		gl.glViewport(0, 0, szer, wys);
		
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		GLU.gluPerspective(gl, 45.0f, (float)szer/(float)wys, 0.1f, 100.0f);
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		GLU.gluLookAt(
				gl, 
				0, 0, 7.5f, //polozenie kamery 
				0, 0, 0, 
				0, 1, 0);		
	}

	public void onSurfaceChanged(GL10 gl, int width, int height) 
	{
		if(height==0) height=1;
		ustawieniaSceny(gl,width,height);		
	}

	private final float a = 1f;
	private float[] tablicaWerteksow=
		{
			//tylnia
			a,-a,-a,
			-a,-a,-a,
			a,a,-a,
			-a,a,-a,			
			//przednia
			-a,-a,a,
			a,-a,a,
			-a,a,a,
			a,a,a,			

			//prawa
			a,-a,a,
			a,-a,-a,
			a,a,a,
			a,a,-a,
			//lewa
			-a,-a,-a,
			-a,-a,a,
			-a,a,-a,
			-a,a,a,

			//gorna
			-a,a,a,
			a,a,a,
			-a,a,-a,
			a,a,-a,
			//dolna
			-a,-a,-a,
			a,-a,-a,
			-a,-a,a,
			a,-a,a
		};
	
	private FloatBuffer buforWerteksow_Polozenie;
	
	private void inicjujBuforWerteksow()
	{
		ByteBuffer vbb=ByteBuffer.allocateDirect(tablicaWerteksow.length*4); //float = 4 bajty
		vbb.order(ByteOrder.nativeOrder());
		buforWerteksow_Polozenie = vbb.asFloatBuffer();
		buforWerteksow_Polozenie.put(tablicaWerteksow);
		buforWerteksow_Polozenie.position(0);
		if(teksturowanie)
		{
			ByteBuffer tbb = ByteBuffer.allocateDirect(wspTeksturowania.length * 4);
			tbb.order(ByteOrder.nativeOrder());
			buforWspolrzednychTeksturowania=tbb.asFloatBuffer();
			buforWspolrzednychTeksturowania.put(wspTeksturowania);
			buforWspolrzednychTeksturowania.position(0);
		}
	}
	
	float kolory[][] =
		{
			{1f,0f,0f,1f}, //tylnia
			{1f,0f,0f,1f}, //przednia
			{0f,1f,0f,1f}, //prawa
			{0f,1f,0f,1f}, //lewa
			{0f,0f,1f,1f}, //gorna
			{0f,0f,1f,1f}, //tylnia
		};
	
	float normalne[][] = 
		{
		{0,0,-1}, //tylnia
		{0,0,1}, //przednia
		{1,0,0}, //prawa
		{-1,0,0}, //lewa
		{0,1,0}, //gorna
		{0,-1,0}, //dolna			
		};
	
	final boolean teksturowanie = true;
	
	float[] wspTeksturowania = 
		{
			//tylnia
			0, 1,
			1, 1,
			0, 0,
			1, 0,
			//przednia
			0, 1,
			1, 1,
			0, 0,
			1, 0,
			//prawa
			0, 1,
			1, 1,
			0, 0,
			1, 0,
			//lewa
			0, 1,
			1, 1,
			0, 0,
			1, 0,
			//gorna
			0, 1,
			1, 1,
			0, 0,
			1, 0,
			//dolna
			0, 1,
			1, 1,
			0, 0,
			1, 0
		};
	
	private FloatBuffer buforWspolrzednychTeksturowania;
	
	int[] textureIDs = new int[1];
	
	public void loadTexture(GL10 gl, Context context)
	{
		gl.glGenTextures(1, textureIDs, 0);
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
		
		InputStream istream = context.getResources().openRawResource(R.drawable.lena);
		Bitmap bitmap;
		try
		{
			bitmap = BitmapFactory.decodeStream(istream);
		}
		finally
		{
			try
			{
				istream.close();				
			}
			catch(IOException e)
			{
			}
		}
		
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
		bitmap.recycle();
	}
	
	private void rysujSzescian(GL10 gl,float krawedz,boolean kolor)
	{
		gl.glFrontFace(GL10.GL_CCW);
		gl.glEnable(GL10.GL_CULL_FACE);
		gl.glCullFace(GL10.GL_BACK);
		
		if(krawedz!=1.0f) gl.glScalef(krawedz, krawedz, krawedz);
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buforWerteksow_Polozenie);
		
		if(teksturowanie)
		{
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, buforWspolrzednychTeksturowania);
		}
		
		if(!kolor) gl.glColor4f(1f, 1f, 1f, 1f);
		
		for(int i=0; i<6;++i)
		{
			gl.glNormal3f(normalne[i][0], normalne[i][1], normalne[i][2]);
			if(kolor) gl.glColor4f(kolory[i][0], kolory[i][1], kolory[i][2], kolory[i][3]);
			gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, i*4, 4);			
		}
		
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisable(GL10.GL_CULL_FACE);
	}
	
	float katX=0.0f;
    float katY=0.0f;
	
    float m[]=new float[]{1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
    
	public void onDrawFrame(GL10 gl) 
	{
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

		//gl.glRotatef(5f, 0f, 1f, 0.1f);
		//gl.glRotatef(katY, 0, 1, 0);
		//gl.glRotatef(katX, 1, 0, 0);
		
		float yx[]=new float[] {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
		Matrix.rotateM(yx,0,katY,0,1,0);
		Matrix.rotateM(yx,0,katX,1,0,0);
		//Matrix.multiplyMM(m, 0, m, 0, yx, 0); //osie lokalne
		Matrix.multiplyMM(m, 0, yx, 0, m, 0); //osie sceny
		
		katX=0.0f;
		katY=0.0f;
		//katX*=0.95f;
		//katY*=0.95f;
		
		gl.glPushMatrix();
		gl.glMultMatrixf(m,0);
		rysujSzescian(gl, 1, false);
		gl.glPopMatrix();
	}
	

	private void oswietlenie(GL10 gl)
	{
		gl.glEnable(GL10.GL_LIGHTING);
		
		mlecznaZarowka(gl,GL10.GL_LIGHT1);
		reflektor(gl, GL10.GL_LIGHT2);
	}
	
	private void mlecznaZarowka(GL10 gl,int zrodloSwiatla)
	{
		final float kolor_rozproszone[]={0.5f,0.5f,0.5f,1.0f};
		gl.glLightfv(zrodloSwiatla, GL10.GL_DIFFUSE, kolor_rozproszone,0);
		gl.glEnable(zrodloSwiatla);
	}
	
	private void reflektor(GL10 gl,int zrodloSwiatla)
	{
		final float kolor_rozproszone[]={0.3f,0.3f,0.3f,1.0f};
		final float kolor_reflektora[]={1f,1f,1f,1.0f};
		final float pozycja[] = {0f,-10f,10f,1f};
		final float szerokosc_wiazki = 60f;
		gl.glLightfv(zrodloSwiatla, GL10.GL_POSITION, pozycja,0);
		gl.glLightfv(zrodloSwiatla, GL10.GL_DIFFUSE, kolor_rozproszone,0);
		gl.glLightfv(zrodloSwiatla, GL10.GL_SPECULAR, kolor_reflektora,0);
		gl.glLightf(zrodloSwiatla, GL10.GL_SPOT_CUTOFF, szerokosc_wiazki);
		gl.glEnable(zrodloSwiatla);
	}

	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub
		
	}

	private SensorManager sensorManager = null;
	Sensor orientacja = null;
	float oz_azymut = 0; //stopnie
	float ox_pochylenie = 0;
	float oy_nachylenie = 0;
	float o_dlugosc = 0;
	
	boolean o_0 = true;
	float ox_0 = 0;
	float oy_0 = 0;
	
	float skalowaniePrzechylenia = 1;
	long czas;
	
	public void onSensorChanged(SensorEvent event) 
	{
		if(event.sensor.getType()==Sensor.TYPE_ORIENTATION)
		{
			oz_azymut=event.values[0];
			ox_pochylenie=event.values[1];
			oy_nachylenie=event.values[2];
			
			if(o_0)
			{
				czas=System.currentTimeMillis();
				ox_0=ox_pochylenie;
				oy_0=oy_nachylenie;
				o_0=false;
			}
			ox_pochylenie-=ox_0;
			oy_nachylenie-=oy_0;
			
			float prog=1; //stopien
			if(Math.abs(ox_pochylenie)<prog) ox_pochylenie=0;
			if(Math.abs(oy_nachylenie)<prog) oy_nachylenie=0;
			
			float _katX=skalowaniePrzechylenia*ox_pochylenie;
			float _katY=skalowaniePrzechylenia*oy_nachylenie;
			
			long dt=System.currentTimeMillis()-czas;
			czas=System.currentTimeMillis();
			if(dt>0)
			{
				_katX/=dt;
				_katY/=dt;
			}
			
			katX-=_katX;
			katY-=_katY;
		}
		
	}
}