package com.roxiga.aquamotion3d;

import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES20;
import android.opengl.Matrix;

public class Model3D extends Sprite2D
{
    protected static final int BONE_DATA_STRIDE_BYTES = 1 * FLOAT_SIZE_BYTES;
    protected FloatBuffer _boneBuffer;
    protected ArrayList<Float>    _bones    = new ArrayList<Float>();
	protected ArrayList<ArrayList<ArrayList<Float>>> _animations = new ArrayList<ArrayList<ArrayList<Float>>>();
    public float[][][] _motions;
	protected int _time = 0;
	public int _currentAnimation = 0;
	public boolean _stopAnimation = false;
	public boolean _loopAnimation = true;
	public boolean _animationLast = false;
    //
	public float _distance = 0;
	public float _intersectU = 0;
	public float _intersectV = 0;

    public Model3D()
    {
    }
    
    protected void init()
    {
    	super.init();
    }
    
    public void draw(Scene3D scene)
    {        
    	if ( !_visible )
    	{
    		return;
    	}
    	GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, _textureID);

        GLES20.glUniform1i(scene._uUseBoneHandle, 0);
        GLES20.glDisableVertexAttribArray(scene._aBoneHandle);

        setTransform();
        Matrix.multiplyMM(_mvpMatrix, 0, scene._vMatrix, 0, _mMatrix, 0);
        Matrix.multiplyMM(_mvpMatrix, 0, scene._projMatrix, 0, _mvpMatrix, 0);
        GLES20.glUniformMatrix4fv(scene._uMVPMatrixHandle, 1, false, _mvpMatrix, 0);

        GLES20.glEnableVertexAttribArray(scene._aPositionHandle);
        _vertexBuffer.position(0);
        GLES20.glVertexAttribPointer(scene._aPositionHandle, 3, GLES20.GL_FLOAT,
        		false,VERTICES_DATA_STRIDE_BYTES, _vertexBuffer);

        GLES20.glEnableVertexAttribArray(scene._aTextureHandle);
        _textureBuffer.position(0);
        GLES20.glVertexAttribPointer(scene._aTextureHandle, 2, GLES20.GL_FLOAT, false,
               UV_DATA_STRIDE_BYTES, _textureBuffer);

        GLES20.glDrawElements(GL10.GL_TRIANGLES, _indices.size(),
        		GLES20.GL_UNSIGNED_SHORT, _indexBuffer);
    }
    
    public Vector3D getPos()
    {
    	return new Vector3D(_pos._x,_pos._y,_pos._z);
    }
	
	public void setAnimation(int i)
	{
		if ( i >= 0 && i < _animations.size() )
		{
			_currentAnimation = i;
			_time = 0;
		}
	}
	
	public void playAnimation(int i,boolean loop,boolean last)
	{
		if ( i >= 0 )
		{
			_currentAnimation = i % _animations.size();
		}
		_time = 0;
		_stopAnimation = false;
        _loopAnimation = loop;
        _animationLast = last;
	}
	
	public void nextAnimation()
	{
		_currentAnimation++;
		if ( _currentAnimation >= _animations.size() )
		{
			_currentAnimation = 0;
		}
		_time = 0;
	}
	
	public boolean intersect(Vector3D origin, Vector3D dir)
	{
        for ( int i = 0; i < _indices.size(); i+=3)
        {
        	Vector3D v0 = _vertices.get(_indices.get(i));
        	Vector3D v1 = _vertices.get(_indices.get(i+1));
        	Vector3D v2 = _vertices.get(_indices.get(i+2));
        	if ( intersectTriangle(origin,dir,v0,v1,v2) )
        	{
        		return true;
        	}
		}
        
		return false;
	}

	private boolean intersectTriangle(
			Vector3D origin, Vector3D dir,
		    Vector3D v0, Vector3D v1, Vector3D v2)
	{
		    Vector3D e1, e2, pv, tv, qv;
		    float det;
		    float t, u, v;
		    float inv_det;

		    e1 = v1.subtract(v0);
		    e2 = v2.subtract(v0);

		    pv = dir.crossProduct(e2);
		    det = e1.dotProduct(pv);

		    if (det > (1e-3))
		    {
		    	tv = origin.subtract(v0);
		        u = tv.dotProduct(pv);
		        if (u < 0.0f || u > det) return false;

		        qv = tv.crossProduct(e1);

		        v = dir.dotProduct(qv);
		        if (v < 0.0 || u + v > det) return false;
		    }
		    else if (det < -(1e-3))
		    {
		        tv = origin.subtract(v0);

		        u = tv.dotProduct(pv);
		        if (u > 0.0 || u < det) return false;

		        qv = tv.crossProduct(e1);

		        v = dir.dotProduct(qv);
		        if (v > 0.0 || u + v < det) return false;

		    }
		    else
		    {
		        return false;
		    }

		    inv_det = 1.0f / det;

		    t = e2.dotProduct(qv);
		    t *= inv_det;
		    u *= inv_det;
		    v *= inv_det;

		    _distance = t;
		    _intersectU = u;
		    _intersectV = v;

		    return true;    //hit!!
	}

    static public float GetDirRadian(Vector3D eye, Vector3D dest)
    {
        float radian;
        Vector3D dir;

        dir = Vector3D.Subtract(dest, eye);

        if (dir._x > 0.0f)
        {
            radian = (float)(-Math.atan(dir._z / dir._x) + Math.PI / 2);
        }
        else if (dir._x < 0.0f)
        {
            radian = (float)(-Math.atan(dir._z / dir._x) - Math.PI / 2);
        }
        else
        {
            return 0;
        }

        return radian;
    }

  	static public float GetDirDegree(Vector3D eye,Vector3D dest)
	{
		float degree;

		degree = (float)Math.toDegrees(GetDirRadian(eye,dest));

		if ( degree < 0 )
		{
			degree += 360;
		}
		if ( degree >= 360 )
		{
			degree -= 360;
		}

		return degree;
	}
    
	public Vector3D forward(float speed,float rotateY)
	{
		Vector3D pos = new Vector3D(0,0,0);
		double radian = Math.toRadians(rotateY);
		pos._x = _pos._x + (float)Math.sin(radian)*speed;
		pos._y = _pos._y;
		pos._z = _pos._z + (float)Math.cos(radian)*speed;
		return pos;
	}

	public void addRotateY(float delta)
	{
		_rotate._y += delta;
		if ( _rotate._y < 0 )
		{
			_rotate._y += 360;
		}
		if ( _rotate._y >= 360 )
		{
			_rotate._y -= 360;
		}
	}

	public Vector2D getScreenPos(Vector3D v,int[] viewport)
	{
		Vector4D v2 = new Vector4D(v._x,v._y,v._z,1);
		Vector4D v3 = v2.Transform(_mvpMatrix);
		if (v3._z == 0.0) return null;
	    v3._x /= v3._w;
	    v3._y /= v3._w;
	    v3._z /= v3._w;
	    /* Map x, y and z to range 0-1 */
	    v3._x = v3._x * 0.5f + 0.5f;
	    v3._y = v3._y * 0.5f + 0.5f;
	    v3._z = v3._z * 0.5f + 0.5f;

	    /* Map x,y to viewport */
	    v3._x = v3._x * viewport[2] + viewport[0];
	    v3._y = v3._y * viewport[3] + viewport[1];

       return new Vector2D(v3._x,viewport[3]-v3._y);
    }
}
