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

namespace Framefield.Core.IDba9ee4dd_6f96_40c9_bb8a_0f383d5cf166
{
    public class Class_Rectangle : 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);
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            try
            {
                UpdateMesh(context, inputs);

                context.Renderer.SetupEffect(context);
                context.Renderer.Render(_rectangleMesh, context);
            }
            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 tessZ = (int) tesselateHeight;
            int numQuads = (tessX - 1)*(tessZ - 1);

            const int attributesSize = 76;
            int numTriangles = numQuads*2;
            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 - 1); ++z)
                {
                    float normalizedBottom = (float) z/(float) (tessZ - 1);
                    float bottom = startZ + (height*normalizedBottom);
                    float normalizedTop = (float) (z + 1)/(float) (tessZ - 1);
                    float top = startZ + (height*normalizedTop);

                    for (int x = 0; x < (tessX - 1); ++x)
                    {
                        float normalizedLeft = (float) (x)/(float) (tessX - 1);
                        float left = startX + (width*normalizedLeft);
                        float normalizedRight = (float) (x + 1)/(float) (tessX - 1);
                        float right = startX + (width*normalizedRight);

                        // tri 1 vert 1
                        vertexStream.Write(new Vector4(right, top, 0, 1));
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(normalizedRight, 1.0f - normalizedTop));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);

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

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

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

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

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

            Changed = false;
        }

        private Mesh _rectangleMesh;
    }
}