using UnityEngine;
using System.Collections;

public class MeshWave : MonoBehaviour {

	// Use this for initialization
    Mesh mesh;
    float[] analysis;
    const int res = 80;
    public float exaggeration = 1.0f;
    int numAnalysisPoints = 1;
    const int nOdfVisible = res;
    int wsize = 1024;
    public  Color col;
    public float RMS = 0.0f;
    public int meshId = 0;
    Manager manager;
    public float spawnFlash = 1.0f;
    GameObject player;
    Vector3 originalSize;
    bool canProcess = false;
    void resampaudio()
    {
        /*
        float[] dat = GetComponent<AudioSource>().GetOutputData(4096, 0);
        float[] resamp = new float[128];

        int blocksz = (4096) / 128;
        
        //resamp audio into 64 vert weights
        for (int i = 0; i <128; i++)
        {
            float avg = 0.0f;
            for (int j = 0; j < blocksz; j++)
            {
                avg += Mathf.Abs(dat[i * blocksz + j]);
            }
            avg /= (float)blocksz;
            resamp[i] = avg;
        }
        */
    }

    public float timeInFile = 0.0f;
    public float timeInFileSmooth = 0.0f;

    public void SetTimeInFile(float val, bool hardcore = false)
    {
        timeInFile = val;
    }

    void UpdateMesh()
    {
        mesh.Clear();

        SetTimeInFile(GetComponent<AudioSource>().time, false);
        float lerptime = player.GetComponent<Player>().alive ? 1.0f : 0.05f;
        timeInFileSmooth = Mathf.Lerp(timeInFileSmooth, timeInFile, lerptime);

        float songTime = timeInFileSmooth;
        float songLength = GetComponent<AudioSource>().clip.length;
        float timerat = songTime / songLength;

        int readStart = (int)(timerat * (float)numAnalysisPoints);
        readStart = Mathf.Clamp(readStart - nOdfVisible/4, 0, numAnalysisPoints - 1);
        int readEnd = Mathf.Clamp(readStart + 3*(nOdfVisible/4), 0, numAnalysisPoints - 1);

        RMS = analysis[readStart + nOdfVisible / 4];

        float[] odf = new float[nOdfVisible];
        int cnt =0;
        for(int i = readStart; i<readEnd;i++)
        {
            odf[cnt] = analysis[i];
            cnt++;
        }
          
        float length = 32f;
        float width = 128.0f;
        int resX = res;
        int resZ = res;

        Vector3[] vertices = new Vector3[resX * resZ];
        for (int z = 0; z < resZ; z++)
        {
            float zPos = ((float)z / (resZ - 1) - .5f) * length;
            for (int x = 0; x < resX; x++)
            {
                // [ -width / 2, width / 2 ]
                float xPos = ((float)x / (resX - 1) - .5f) * width;
                float distfromCenter = 1.0f - Mathf.Abs(x - res / 4) / res/2;
                float accentuate = Mathf.Pow(distfromCenter, 4.0f);
                accentuate *= RMS * 4.0f;
                accentuate += 1.0f;
                float edgeRoll = Mathf.Abs(z - res/2) / (res/2f);
                edgeRoll *= 4.0f;
                float totalacc = accentuate;

                float sine = Mathf.Sin(xPos*(0.4f+RMS) - Time.time*60.0f)*2.0f*(RMS*1.9f + 0.1f);
                vertices[x + z * resX] = new Vector3(xPos, odf[x] * exaggeration * 2.4f * accentuate + sine, zPos);
            }
        }

        Vector3[] normals = new Vector3[vertices.Length];
        for (int n = 0; n < normals.Length; n++)
            normals[n] = Vector3.up;

        Vector2[] uvs = new Vector2[vertices.Length];
        for (int v = 0; v < resZ; v++)
        {
            for (int u = 0; u < resX; u++)
            {
                uvs[u + v * resX] = new Vector2((float)u / (resX - 1), (float)v / (resZ - 1));
            }
        }

        int nbFaces = (resX - 1) * (resZ - 1);
        int[] triangles = new int[nbFaces * 6];
        int t = 0;
        for (int face = 0; face < nbFaces; face++)
        {
            // Retrieve lower left corner from face ind
            int i = face % (resX - 1) + (face / (resZ - 1) * resX);

            triangles[t++] = i + resX;
            triangles[t++] = i + 1;
            triangles[t++] = i;

            triangles[t++] = i + resX;
            triangles[t++] = i + resX + 1;
            triangles[t++] = i + 1;
        }

        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.uv = uvs;
        mesh.triangles = triangles;

        mesh.RecalculateBounds();
        GetComponent<Renderer>().material.SetColor("_Col", col);
        GetComponent<Renderer>().material.SetFloat("_Intensity",RMS);
        GetComponent<Renderer>().material.SetFloat("_SpawnFlash", spawnFlash);

        mesh.Optimize();
    }

    void Analyze()
    {
        AudioClip clip = GetComponent<AudioSource>().clip;
        float[] dat = new float[clip.samples*clip.channels];
        clip.GetData(dat, 0);
        int nblocks = clip.samples / wsize;
        analysis = new float[nblocks];
        int count = 0;
        for (int i = 0; i < nblocks; i++)
        {
            float blockamp = 0.0f;

            int start = i * wsize * clip.channels;

            //just sum everything on all channels in this range I guess
            for (int j = start; j < start + wsize * clip.channels; j += 1)
            {
                blockamp += Mathf.Abs(dat[j]);
            }

            blockamp = blockamp / (float)wsize;
            analysis[i] = blockamp;
            count++;
        }

        numAnalysisPoints = nblocks;
    }

	void Start () {
        // You can change that line to provide another MeshFilter
        //delete the original mesh filter
        manager = GameObject.Find("Manager").GetComponent<Manager>();

        MeshFilter filter = GetComponent<MeshFilter>();
        mesh = filter.mesh;

        player = GameObject.Find("Player");
        Analyze();
        originalSize = transform.localScale;
	}

    // Update is called once per frame
    void Update()
    {
        bool playerStarted = player.GetComponent<Player>().started;

        canProcess = meshId <= manager.nLanes;
        if (!playerStarted) canProcess = false;

        GetComponent<Renderer>().enabled = canProcess;

        if (canProcess)
        {
            spawnFlash *= 0.9f;
            transform.localScale = originalSize * (1.0f - spawnFlash);
            UpdateMesh();
        }
        else spawnFlash = 1.0f;
	}

    void LateUpdate()
    {
        AudioSource aus = GetComponent<AudioSource>();
       // if (aus.time < 0.5f && aus.pitch < 1.0f)
       //     aus.pitch = 1.0f;
        
    }
}
