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

namespace Framefield.Core.ID5c9afeab_5182_4521_bf37_28a351b104e8
{
    public class Class_Grid : OperatorPart.Function,
                                   Framefield.Core.OperatorPartTraits.IMeshSupplier
    {
        // 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)
        {
            try
            {
                UpdateMesh(context, inputs);

                var rasterizerDescription = new RasterizerStateDescription();
                rasterizerDescription.FillMode = (FillMode) 2; // Wireframe
                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)
        {
            if (_rectangleMesh != null && !Changed)
                return;

            // params >>>
            var height = inputs[0].Eval(context).Value;
            var width = inputs[1].Eval(context).Value;
            var ColorA = inputs[2].Eval(context).Value;
            var ColorB = inputs[3].Eval(context).Value;
            var ColorG = inputs[4].Eval(context).Value;
            var ColorR = inputs[5].Eval(context).Value;
            var tesselateWidth = inputs[6].Eval(context).Value;
            var tesselateHeight = inputs[7].Eval(context).Value;
            // <<< params

            // 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);

            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 startX = -width/2.0f;
                //float startZ = -height/2.0f;

                //for (int z = 0; z < (tessZ ); ++z)
                //{
                    //float normalizedBottom = (float) z/(float) (tessZ-1);
                    //float bottom = startZ + (height*normalizedBottom);
                    //float normalizedTop = (float) (z +1)/(float) (tessZ );
                    //float top = startZ + (height*normalizedTop);

                    float bottom= -height/2;
                    float top=    height/2;
                    float left = -width/2;
                    float right = width/2;
                    
                    for (int cx = 0; cx <= tessX; ++cx)
                    {                    
                        float normalizedRight = (float)cx/tessX;
                        float x= left+ width* normalizedRight;

                        // tri 1 vert 1
                        vertexStream.Write(new Vector4(x,top, 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(x, bottom, 0, 1));
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(normalizedRight, 1.0f));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
                    }
                    
                    for (int cy = 0; cy <= tessY  ; ++cy)
                    {          
                        float normalizedBottom = (float)cy/tessY;
                        float y= bottom + height* normalizedBottom;


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

                        // tri 2 vert 2
                        vertexStream.Write(new Vector4(left, y, 0, 1));
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(0, 1.0f - normalizedBottom));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);

                        // tri 2 vert 3
                        vertexStream.Write(new Vector4(right, y, 0, 1));
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(0, 1.0f - normalizedBottom));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);                                          
                    }
                //}
            }
            context.D3DDevice.ImmediateContext.UnmapSubresource(_rectangleMesh.Vertices, 0);
            // <<< Function

            Changed = false;
        }

        private Mesh _rectangleMesh;
    }
}