using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

using EVE.EVEShaders;

namespace EVE.EVEGpgpu
{
    public class EVENavierStokes
    {
        RenderTarget2D velocityArray;
        RenderTarget2D pressureArray;
        RenderTarget2D divergenceArray;
        RenderTarget2D tempArray;

        RenderTarget2D debugArray;

        EVEGpKernel computeKernel;

        int cellNo;

        double dTime;
        int simStep;
        Vector2 lastMPos;

        GraphicsDevice currentProcessor;

        public EVENavierStokes(GraphicsDevice myDevice)
        {
            currentProcessor = myDevice;

            EVEGpKernel baseKernel;
            baseKernel = new EVEGpKernel(@"Content/Shaders/GPGPU_TEST");
            EVEShaderManager.AddKernel("GPGPU_TEST", baseKernel);

            baseKernel = new EVEGpKernel(@"Content/Shaders/GPGPU_INIT");
            EVEShaderManager.AddKernel("GPGPU_INIT", baseKernel);

            baseKernel = new EVEGpKernel(@"Content/Shaders/DEBUG_DRAW");
            EVEShaderManager.AddKernel("DEBUG_DRAW", baseKernel);

            baseKernel = new EVEGpKernel(@"Content/Shaders/GPGPU/GPGPU_NavierStokes");
            EVEShaderManager.AddKernel("GPGPU_NavierStokes", baseKernel);

            cellNo = 128;

            velocityArray = new RenderTarget2D(myDevice, cellNo, cellNo, 1, SurfaceFormat.Vector4);
            pressureArray = new RenderTarget2D(myDevice, cellNo, cellNo, 1, SurfaceFormat.Vector4);
            divergenceArray = new RenderTarget2D(myDevice, cellNo, cellNo, 1, SurfaceFormat.Vector4);
            tempArray = new RenderTarget2D(myDevice, cellNo, cellNo, 1, SurfaceFormat.Vector4);
            debugArray = new RenderTarget2D(myDevice, cellNo, cellNo, 1, SurfaceFormat.Vector4);

            dTime = 0;
        }

        public void PerformArrayInit(Vector4 initVal)
        {
            simStep = 0;
            lastMPos = new Vector2(0.0f,0.0f);

            computeKernel = EVEShaderManager.GetShader("GPGPU_NavierStokes") as EVEGpKernel;
            computeKernel.SetTechnique("Init");
            computeKernel.myEffect.Parameters["initValue"].SetValue(new Vector4(0,0,0,0));
            computeKernel.CalculateSingleKernel(currentProcessor, "Init", tempArray, velocityArray);

            computeKernel.myEffect.Parameters["initValue"].SetValue(new Vector4(0, 0, 0, 0));
            computeKernel.CalculateSingleKernel(currentProcessor, "Init", tempArray, pressureArray);
        }
        
        public void PerformCalculation()
        {

            //computeKernel.PerformCopy(currentProcessor, velocityArray, debugArray);
            computeKernel.myEffect.Parameters["halfTexel"].SetValue((float)(1.0f /(float)cellNo));
            

                //computeKernel.SetTechnique("Init");
                //computeKernel.myEffect.Parameters["initValue"].SetValue(new Vector4(1, 0, 1, 0));
                //velocity = computeKernel.CalculateSingleKernel(currentProcessor, baseCalcArray);

            //
            // Advection
            //
            //Console.Out.WriteLine(dTime);
            //computeKernel = EVEShaderManager.GetShader("GPGPU_NavierStokes") as EVEGpKernel;

            //computeKernel.SetTechnique("DrawVelocity");
            //computeKernel.PerformDebugDraw(currentProcessor, velocity);

            computeKernel.SetTechnique("AdvectVel");
            computeKernel.myEffect.Parameters["dTime"].SetValue((float)dTime);
                               
            //if(simStep%2==0)
            computeKernel.CalculateSingleKernel(currentProcessor,"AdvectVel", velocityArray, tempArray, velocityArray);
            //else
            //computeKernel.SetTechnique("Divergence");
            //velocity = computeKernel.CalculateSingleKernel(currentProcessor, velocity, baseCalcArray1);

                        //computeKernel.PerformDebugDraw(currentProcessor,"DrawVelocity",velocityArray);
            //
            //Divergence
            //
            computeKernel.SetTechnique("Divergence");
            computeKernel.CalculateSingleKernel(currentProcessor, "Divergence", velocityArray, tempArray, divergenceArray);

            //computeKernel.SetTechnique("DrawVelocity");
            //computeKernel.PerformDebugDraw(currentProcessor, velocity);

            //
            //Jacobi Iteration
            //

            computeKernel.SetTechnique("JacobiIteration");
            for (int i = 0; i < 20; i++)
            {
                //if(i%2==0)
                computeKernel.CalculateSingleKernel(currentProcessor,"JacobiIteration", divergenceArray, pressureArray, tempArray, pressureArray);
                //else
                //pressure = computeKernel.CalculateSingleKernel(currentProcessor, divergence, pressure, baseCalcArray2);
            }

            computeKernel.PerformCopy(currentProcessor, pressureArray, debugArray);

            computeKernel.SetTechnique("DrawVelocity");
            //computeKernel.PerformDebugDraw(currentProcessor, pressure);

            //computeKernel.SetTechnique("Init");

            //computeKernel.myEffect.Parameters["initValue"].SetValue(new Vector4(1, 0, 0, 0));
            //pressure = computeKernel.CalculateSingleKernel(currentProcessor, baseCalcArray1);

            //p - 2
            //d - 0
            //v - 3

            //
            //Projection
            //

            computeKernel.SetTechnique("Project");
            computeKernel.CalculateSingleKernel(currentProcessor,"Project", velocityArray, pressureArray, tempArray, velocityArray);

            MouseState ms = Mouse.GetState();
            Vector2 aMPos = new Vector2((float)(ms.X * 0.002f), (float)(ms.Y * 0.002f));
            Vector2 velForce = lastMPos - aMPos;
            computeKernel.myEffect.Parameters["velForce"].SetValue(velForce);
            computeKernel.myEffect.Parameters["Point"].SetValue(aMPos);

            computeKernel.SetTechnique("AddVelocityInfluence");

            //if (simStep % 2 == 0)
                computeKernel.CalculateSingleKernel(currentProcessor, "AddVelocityInfluence", velocityArray, tempArray, velocityArray);
            //else
            //    velocity = computeKernel.CalculateSingleKernel(currentProcessor, velocity, baseCalcArray1);

                //computeKernel.SetTechnique("DrawVelocity");
                computeKernel.PerformDebugDraw(currentProcessor,"DrawVelocity", debugArray);
            
            lastMPos = aMPos;
            simStep++;

        }

        public void PerformDebugDraw()
        {
            /*
            //testKernel = EVEShaderManager.GetShader("DEBUG_DRAW") as EVEGpKernel;
            //testKernel.PerformDebugDraw(currentProcessor, inArray);
            */

            computeKernel = EVEShaderManager.GetShader("GPGPU_NavierStokes") as EVEGpKernel;
            computeKernel.SetTechnique("DrawVelocity");
            //computeKernel.PerformDebugDraw(currentProcessor, pressure);            
        }

        public void PerformFullStep(GameTime gameTime)
        {
            dTime = 0.0001*gameTime.ElapsedGameTime.Milliseconds;
            PerformCalculation();
            //PerformDebugDraw();
        }
    }
}
