using UnityEngine;
using System.Collections;


// class that transform thunder data into a mesh/vertex
// faster than creating and using RenderLine



/*public class ThunderStrikeSimple : ThunderStrike
{
    static Perlin oPerlinNoise;

    public ThunderStrikeSimple(int iNbSection) : base( iNbSection )
    {
        if ( oPerlinNoise == null)
        {
            oPerlinNoise = new Perlin();
        }
    }

    public virtual void InitPath( Transform oStartPoint, Transform oEndPoint )
    {
        // trace the path between each point
        for (int i = 0; i < m_vPathPointArray.Length; i++)
        {
            float fd = ((float)i) / (float)(m_vPathPointArray.Length - 1);

            m_vOriginPathPointArray[i] = new Vector3();
            m_vOriginPathPointArray[i] = oStartPoint.position * (1.0f - fd) + oEndPoint.position * fd;

            m_vPathPointArray[i] = m_vOriginPathPointArray[i];
        }
    }

    public override void UpdateThunder()
    {
        float speed = 30.0f;

        float timex = Time.time * speed * 0.1365143f ;
        float timey = Time.time * speed * 1.21688f ;
        float timez = Time.time * speed * 2.5564f ;


        for (int i = 0; i < m_vPathPointArray.Length; i++)
        {
            Vector3 offset = new Vector3(oPerlinNoise.Noise(timex + m_vOriginPathPointArray[i].x, timex + m_vOriginPathPointArray[i].y, timex + m_vOriginPathPointArray[i].z),
                oPerlinNoise.Noise(timey + m_vOriginPathPointArray[i].x, timey + m_vOriginPathPointArray[i].y, timey + m_vOriginPathPointArray[i].z),
                oPerlinNoise.Noise(timez + m_vOriginPathPointArray[i].x, timez + m_vOriginPathPointArray[i].y, timez + m_vOriginPathPointArray[i].z));

            m_vPathPointArray[i] = m_vOriginPathPointArray[i] + offset;
        }
    }
}*/



public class Thunder : MonoBehaviour
{

    // configuration ( exposed )
    public Material     oThunderMaterial = null;

    public int          iThunderNBStrike = 10;

    public int          iThunderNbSection = 10;

    public float        fThunderThickness = 0.5f;

    public Color        oThunderColor = Color.white;
	
	public float		fLightIntensity = 1.0f;
	
    // ref of component
    MeshFilter oMeshFilter 		= null;
    MeshRenderer oMeshRenderer 	= null;
    Mesh oMeshFromMeshFilter 	= null;
	[HideInInspector]
	public Light oLight			 	= null;

    // Mesh data
    int m_iNbVertexNeeded;
    int m_iNbTriangleNeeded;
	
	Vector3[] 	vertices 	= null;
    Color[] 	colors 		= null;
    Vector2[] 	uv 			= null;
    int[] 		oTriangles 	= null;
	
	// thunder array ( use to construct the mesh )
	[HideInInspector]
    public ThunderStrike[] oThunderStrikeArray;
	
	// thunder generator
	ThunderGenerator oThunderGenerator = null;
	
	// put the flag to true, to update on the next frame, the mesh for any change the generator made to the thunder
	[HideInInspector]
	public bool bUpdateColor    = false;
	
	[HideInInspector]
	public bool bUpdateUV       = false;
	
	[HideInInspector]
	public bool bUpdateVertices = false;
	
	
	[HideInInspector]
	public bool bUpdateAll      = false; // call this when the number of Section or number of thunder change ( warning cost some CPU power )	
	
	
	// old value to detect any change
	int          iOldThunderNBStrike;
    int          iOldThunderNbSection;
    float        fOldThunderThickness;
	Color        oOldThunderColor;
	
	
	//--------------------------------------------------------------------------------------
    // initialization
    void Start()
    {
		#if UNITY_EDITOR
			// verfy the material has been set
	        if ( oThunderMaterial == null )
	        {
	            Debug.LogError("No material");
	            return;
	        }
		#endif		
		
		// find the thunder generator
		oThunderGenerator = gameObject.GetComponent<ThunderGenerator>();
			
		#if UNITY_EDITOR
			// verfy the ThunderGenerator  has been set
	        if ( oThunderGenerator == null )
	        {
	            Debug.LogError("No Thunder Generator, you must add a thunder generator script component tho this gameobject");
	            return;
	        }
		#endif
		
        // Add necessary Mesh
        if ( oMeshFilter == null )
        {
            oMeshFilter = gameObject.AddComponent<MeshFilter>();
            oMeshFromMeshFilter = oMeshFilter.mesh;
        }

        if ( oMeshRenderer == null )
        {
            oMeshRenderer = gameObject.AddComponent<MeshRenderer>();

            oMeshRenderer.material = oThunderMaterial;
        }
		
		// add light if necessary
		oLight = gameObject.GetComponent<Light>();
		
		if ( oLight == null )
        {
            oLight = gameObject.AddComponent<Light>();
        }
		
		// update strike array
		UpdateNbStrike();
		
		// construct the mesh
		UpdateAllMeshArray();
		
		 // link thunder generator
		oThunderGenerator.InitThunders();
		
        // set the transform to 0.0.0, to use the local position as worldposition, and avoid transforming point
        gameObject.transform.position   = new Vector3(0.0f, 0.0f, 0.0f);
        gameObject.transform.rotation   = Quaternion.identity;
        gameObject.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
		
    }
	
	void UpdateNbStrike()
	{        
		oThunderStrikeArray = new ThunderStrike[iThunderNBStrike];

        for ( int i = 0 ; i < iThunderNBStrike ; i++ )
        {
            ThunderStrike oThunderStrike = new ThunderStrike( iThunderNbSection );

            oThunderStrikeArray[i] = oThunderStrike;
        }
		
		oThunderGenerator.SetThunder( this );
		
		// set old value 
		iOldThunderNBStrike  = iThunderNBStrike;
	}
	
	//--------------------------------------------------------------------------------------
	void UpdateAllMeshArray()
	{
		// calculate Nb vertex & triangle needed
        m_iNbVertexNeeded = 0;
        m_iNbTriangleNeeded = 0;

        for ( int i = 0 ; i < iThunderNBStrike ; i++ ) // keep this code complexity, in case we want different quality for each thunder
        {
            m_iNbVertexNeeded += oThunderStrikeArray[i].m_iNbSection;

            m_iNbTriangleNeeded += (oThunderStrikeArray[i].m_iNbSection - 1) * 6; // 6 = ( 2 * 3 ) = ( 2 triangles - each triangle composed of 3 vertices )
        }

        m_iNbVertexNeeded *= 2;
		
		// create arrays
	    vertices 	= new Vector3[m_iNbVertexNeeded];
        colors 		= new Color  [m_iNbVertexNeeded];
        uv 			= new Vector2[m_iNbVertexNeeded];
		
        oTriangles 	= new int	 [m_iNbTriangleNeeded];	
		
		oMeshFromMeshFilter.Clear();
		
		// update all array value
		UpdateTriangles();
		UpdateVertices();
		UpdateColor();
		UpdateUV();

		bUpdateAll      = false;
		
		// set old value 
    	iOldThunderNbSection = iThunderNbSection;
	}
	
	//--------------------------------------------------------------------------------------
	// update triangle array
	void UpdateTriangles()
	{
        int iContinuVertexIndex = 0;
        int iContinuTriangleIndex = 0;

        // create triangles array for each thunder
        for ( int i = 0; i < oThunderStrikeArray.Length; i++)
        {
            ThunderStrike oCurrentThunderStrinke = oThunderStrikeArray[i];

            int iNbSection = oCurrentThunderStrinke.m_iNbSection;

            // Generate triangles indices
            int iNbTriangleIndice = (iNbSection - 1) * 6;// 6 = ( 2 * 3 ) = ( 2 triangles - each triangle composed of 3 vertices )

            for ( int k = 0; k < iNbTriangleIndice / 6; k++)
            {
                int iCurrentTrianglesStartIndex = iContinuTriangleIndex + k * 6;
				
				int kMult2 = k * 2;
					
                oTriangles[iCurrentTrianglesStartIndex + 0] = iContinuVertexIndex + kMult2;
                oTriangles[iCurrentTrianglesStartIndex + 1] = iContinuVertexIndex + kMult2 + 1;
                oTriangles[iCurrentTrianglesStartIndex + 2] = iContinuVertexIndex + kMult2 + 2;

                oTriangles[iCurrentTrianglesStartIndex + 3] = iContinuVertexIndex + kMult2 + 2;
                oTriangles[iCurrentTrianglesStartIndex + 4] = iContinuVertexIndex + kMult2 + 1;
                oTriangles[iCurrentTrianglesStartIndex + 5] = iContinuVertexIndex + kMult2 + 3;
				
            }
			
			iContinuVertexIndex   += iNbSection * 2;
            iContinuTriangleIndex += iNbTriangleIndice;
        }
		
	}
	
	//--------------------------------------------------------------------------------------
	// update Vertices array
	void UpdateVertices()
	{
        int iContinuVertexIndex = 0;
		
		float fCurrentThickness = 1.0f;
		
        // create vertex array for each thunder
        for ( int i = 0; i < oThunderStrikeArray.Length; i++)
        {
            ThunderStrike oCurrentThunderStrinke = oThunderStrikeArray[i];

            int iNbSection = oCurrentThunderStrinke.m_iNbSection;
			
			// axis align bilboard ( use only start-end vector thunder as an aproximation, and don't calculate it for each triangle ).
            Vector3 vUpDir = Vector3.Cross( oCurrentThunderStrinke.m_vPathPointArray[0] - Camera.main.transform.position, (oCurrentThunderStrinke.m_vPathPointArray[0] - oCurrentThunderStrinke.m_vPathPointArray[iNbSection - 1]) );
            vUpDir.Normalize();

            for ( int j = 0; j < iNbSection; j++ )
            {
				if ( oCurrentThunderStrinke.m_fParticularThicknees == -1.0f )
				{
					fCurrentThickness = fThunderThickness; // use the global thickness
				}
				else
				{
					fCurrentThickness = oCurrentThunderStrinke.m_fParticularThicknees; 
				}
				
                vertices[iContinuVertexIndex + j * 2 + 0] = oCurrentThunderStrinke.m_vPathPointArray[j] - vUpDir * fCurrentThickness;
                vertices[iContinuVertexIndex + j * 2 + 1] = oCurrentThunderStrinke.m_vPathPointArray[j] + vUpDir * fCurrentThickness;
            }

            iContinuVertexIndex   += iNbSection * 2;
        }
		
		bUpdateVertices = false;
		
		// set old value 
		fOldThunderThickness = fThunderThickness;
	}
	
	//--------------------------------------------------------------------------------------
	// update Color array
	void UpdateUV()
	{
		int iContinuVertexIndex = 0;

        // create uv array for each thunder
        for ( int i = 0; i < oThunderStrikeArray.Length; i++)
        {
            ThunderStrike oCurrentThunderStrinke = oThunderStrikeArray[i];

            int iNbSection = oCurrentThunderStrinke.m_iNbSection;

            for ( int j = 0; j < iNbSection; j++ )
            {
                uv[iContinuVertexIndex + j * 2 + 0] = new Vector2(((float)j) / ( iNbSection - 1),  0);
                uv[iContinuVertexIndex + j * 2 + 1] = new Vector2(((float)j) / ( iNbSection - 1 ), 1);
            }

            iContinuVertexIndex   += iNbSection * 2;
        }
		
		bUpdateUV = false;
		
	}
	
	//--------------------------------------------------------------------------------------
	// update UV array
	void UpdateColor()
	{
		int iContinuVertexIndex = 0;

        // create color array for each thunder
        for ( int i = 0; i < oThunderStrikeArray.Length; i++)
        {
            ThunderStrike oCurrentThunderStrinke = oThunderStrikeArray[i];

            int iNbSection = oCurrentThunderStrinke.m_iNbSection;

            for ( int j = 0; j < iNbSection; j++ )
            {
                colors[iContinuVertexIndex + j * 2 + 0] = oThunderColor;
                colors[iContinuVertexIndex + j * 2 + 1] = oThunderColor;
            }
			
            iContinuVertexIndex   += iNbSection * 2;
        }
		
		//update light color
		oLight.color = oThunderColor;
		
		bUpdateColor = false;
		
		// update old value
		oOldThunderColor 	 = oThunderColor;
	}

	//--------------------------------------------------------------------------------------
    // Update is called once per frame
    void Update()
    {
		
		#if UNITY_EDITOR
			// verification
	        if ( oMeshFromMeshFilter == null )
	        {
	            Debug.LogError("No MeshFromMeshFilter created");
	            return;
	        }
			
			if ( oMeshRenderer == null )
	        {
	            Debug.LogError("No MeshRenderer created");
	            return;
	        }
		
			if ( oThunderGenerator == null )
	        {
	            Debug.LogError("No ThunderGenerator script");
	            return;
	        }
		
			if ( oLight == null )
	        {
	            Debug.LogError("No oLight created");
	            return;
	        }
		
		#endif	
		
		// detect any real time change, and rebuild thunder in necessary
		if ( ( iOldThunderNBStrike != iThunderNBStrike ) || ( iOldThunderNbSection != iThunderNbSection) )
		{
			UpdateNbStrike();
			oThunderGenerator.InitThunders();
			bUpdateAll = true;
		}
		else		
		{
			if ( fOldThunderThickness != iThunderNBStrike )
			{
				bUpdateVertices = true;
			}
			
			if ( oOldThunderColor != oThunderColor )
			{
				bUpdateColor = true;
			}	
		}
		
		// update thunder
		oThunderGenerator.UpdateThunders();
		

		// make necessay update
		if ( bUpdateAll )
		{			
			UpdateAllMeshArray();
		}
		else
		{
			if ( bUpdateColor )
			{
				UpdateColor();
			}
			
			if ( bUpdateUV )
			{
				UpdateUV();
			}
			
			if ( bUpdateVertices )
			{
				UpdateVertices();
			}
		}

        // Assign to mesh	
        oMeshFromMeshFilter.vertices = vertices;
        oMeshFromMeshFilter.colors = colors;
        oMeshFromMeshFilter.uv = uv;
        oMeshFromMeshFilter.triangles = oTriangles;

    }
}