using System;
using System.Collections.Generic;
using SharpDX;
using SharpDX.Direct3D11;
using Buffer = SharpDX.Direct3D11.Buffer;

/*  
    v.01
    Michael Auerswald <michael@908video.de>

    Modified "Grid" operator.
    
    Reads float[512] FFT data from a BassLive operator and visualizes the bands using simple geometry
*/

namespace Framefield.Core.ID0c6046a9_758c_4deb_a367_be43a766a218
{
    public class Class_BassFFTVisualize : OperatorPart.Function,
                                   Framefield.Core.OperatorPartTraits.IMeshSupplier
    {
    
        //>>> _inputids
        private enum InputId
        {
            SizeHeight = 0,
            SizeWidth = 1,
            ColorA = 2,
            ColorB = 3,
            ColorG = 4,
            ColorR = 5,
            FFTdata = 6,
            Multiplier = 7
        }
        //<<< _inputids
        
        // IMeshSupplier
        public void AddMeshesTo(ICollection<Mesh> meshes)
        {
            UpdateMesh(new OperatorPartContext(), OperatorPart.Connections);
            meshes.Add(_rectangleMesh);
        }

        public override void Dispose()
        {
            Utilities.DisposeObj(ref _rectangleMesh);
        }

        private RasterizerState _rasterizerState = null;
        
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            var FFTdata = inputs[(int)InputId.FFTdata].Eval(context).Dynamic;  // Needs to be checked for null!
            if (FFTdata == null) return context;
            
            try
            {
                UpdateMesh(context, inputs);

                var rasterizerDescription = new RasterizerStateDescription();
                rasterizerDescription.FillMode = (FillMode) 3; // Wireframe/Fill
                rasterizerDescription.CullMode = (CullMode) 1; // None
                rasterizerDescription.IsMultisampleEnabled =false;

                Utilities.DisposeObj(ref _rasterizerState);
                _rasterizerState = new RasterizerState(context.D3DDevice, rasterizerDescription);
                
                var prevRasterizerState = context.RasterizerState;
                context.RasterizerState = _rasterizerState;
               
                context.Renderer.SetupEffect(context);
                context.Renderer.Render(_rectangleMesh, context);
                context.RasterizerState = prevRasterizerState;
            }
            catch (Exception)
            {
                Logger.Error(this,"error");
            }

            return context;
        }

        private void UpdateMesh(OperatorPartContext context, IList<OperatorPart> inputs)
        {


            //>>> _params
            var SizeHeight = inputs[(int)InputId.SizeHeight].Eval(context).Value;
            var SizeWidth = inputs[(int)InputId.SizeWidth].Eval(context).Value;
            var Size = new Vector2(SizeHeight, SizeWidth);
            var ColorA = inputs[(int)InputId.ColorA].Eval(context).Value;
            var ColorB = inputs[(int)InputId.ColorB].Eval(context).Value;
            var ColorG = inputs[(int)InputId.ColorG].Eval(context).Value;
            var ColorR = inputs[(int)InputId.ColorR].Eval(context).Value;
            var Color = new Vector4(ColorA, ColorB, ColorG, ColorR);
            var FFTdata = inputs[(int)InputId.FFTdata].Eval(context).Dynamic;  // Needs to be checked for null!
            var Multiplier = inputs[(int)InputId.Multiplier].Eval(context).Value;
            //<<< _params

            if (_rectangleMesh != null && !Changed)
                return;

            // >>> Function
            var normal = new Vector3(0.0f, 0.0f, -1.0f);
            var color = new Vector4(ColorR, ColorG, ColorB, ColorA);
            var tangent = new Vector3(1.0f, 0.0f, 0.0f);
            var binormal = new Vector3(0.0f, -1.0f, 0.0f);

            var inputElements = new InputElement[]
                                    {
                                        new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 0, 0),
                                        new InputElement("NORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 16, 0),
                                        new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 28, 0),
                                        new InputElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, 44, 0),
                                        new InputElement("TANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, 52, 0),
                                        new InputElement("BINORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 64, 0)
                                    };

            //int tessX = (int) tesselateWidth;
            //int tessY = (int) tesselateHeight;
            //int numTriangles = (tessX+1) + (tessY+1);
            int numTriangles = _SAMPLES * 2;
            

            const int attributesSize = 76;
            //int numTriangles = numQuads*1;
            int streamSize = numTriangles*3*attributesSize;

            if (_rectangleMesh == null || streamSize != _rectangleMesh.NumTriangles*3*_rectangleMesh.AttributesSize)
            {
                Utilities.DisposeObj(ref _rectangleMesh);
                using (var stream = new DataStream(streamSize, true, true))
                {
                    var vertices = new Buffer(context.D3DDevice, stream, new BufferDescription
                                                                             {
                                                                                 BindFlags = BindFlags.VertexBuffer,
                                                                                 CpuAccessFlags = CpuAccessFlags.Write,
                                                                                 OptionFlags = ResourceOptionFlags.None,
                                                                                 SizeInBytes = streamSize,
                                                                                 Usage = ResourceUsage.Dynamic
                                                                             });
                    _rectangleMesh = new Mesh
                                         {
                                             InputElements = inputElements,
                                             Vertices = vertices,
                                             NumTriangles = numTriangles,
                                             AttributesSize = attributesSize
                                         };
                }
            }
            DataStream vertexStream;
            context.D3DDevice.ImmediateContext.MapSubresource(_rectangleMesh.Vertices, MapMode.WriteDiscard, MapFlags.None, out vertexStream);
            using (vertexStream)
            {
                vertexStream.Position = 0;

                float bottom= -SizeHeight/2;
                float top=    SizeHeight/2;
                float left = -SizeWidth/2;
                float right = SizeWidth/2;
                
                for (int cx = 0; cx <= _SAMPLES-1; ++cx)
                {                    
                    float normalizedRight = (float)cx/_SAMPLES;
                    float normalizedRightNext = (float)(cx+1)/_SAMPLES;
                    float x= left + SizeWidth* normalizedRight;
                    float xNext= left + SizeWidth* normalizedRightNext;

                    float fft = FFTdata[cx] * Multiplier;

                    // tri 1 vert 1
                    vertexStream.Write(new Vector4(x,top+fft, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f ));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 2
                    vertexStream.Write(new Vector4(x, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 3
                    vertexStream.Write(new Vector4(xNext, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);


                    // tri 1 vert 1
                    vertexStream.Write(new Vector4(x,top+fft, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f ));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 2
                    vertexStream.Write(new Vector4(xNext, top+fft, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);

                    // tri 1 vert 3
                    vertexStream.Write(new Vector4(xNext, bottom, 0, 1));
                    vertexStream.Write(normal);
                    vertexStream.Write(color);
                    vertexStream.Write(new Vector2(normalizedRight, 1.0f));
                    vertexStream.Write(tangent);
                    vertexStream.Write(binormal);
                }

            }
            context.D3DDevice.ImmediateContext.UnmapSubresource(_rectangleMesh.Vertices, 0);
            //<<< Function

            Changed = false;
        }

        private Mesh _rectangleMesh;
        const int _SAMPLES = 512; // hardcoded number of fft samples
    }
}