//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
using Framefield.Core.IDb03022e3_8b4f_4d33_b749_ef30a03ef098; // CurveProviderFunc
//<<< _using


using Buffer = SharpDX.Direct3D11.Buffer;

namespace Framefield.Core.IDadcaf21d_fa1b_4fa6_a4d7_b525554d95e4
{
    public class Class_CurveGeometry : OperatorPart.Function
    {
        //>>> _inputids
        private enum InputId
        {
            Steps = 0,
            UVMode = 1,
            Width = 2,
            UpVectorX = 3,
            UpVectorY = 4,
            UpVectorZ = 5,
            ColorR = 6,
            ColorG = 7,
            ColorB = 8,
            ColorA = 9,
            Twist = 10,
            RangeStart = 11,
            RangeEnd = 12,
            Curve = 13
        }
        //<<< _inputids

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

        
        float _width;
        int _steps = 10;
        int _uvMode;
        int _paintedUpdates = 0;
        float _textureScale;
        float _lastTextureU;
        float _minDistance;
        float _paintedLength;
        float _twist;
        int _numControlPoints=1;
        Vector3 _up;
        
        ICurveProvider  _curve;
        
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {

            //>>> _params
            var Steps = inputs[(int)InputId.Steps].Eval(context).Value;
            var UVMode = (int) inputs[(int)InputId.UVMode].Eval(context).Value;
            var Width = inputs[(int)InputId.Width].Eval(context).Value;
            var UpVectorX = inputs[(int)InputId.UpVectorX].Eval(context).Value;
            var UpVectorY = inputs[(int)InputId.UpVectorY].Eval(context).Value;
            var UpVectorZ = inputs[(int)InputId.UpVectorZ].Eval(context).Value;
            var UpVector = new Vector3(UpVectorX, UpVectorY, UpVectorZ);
            var ColorR = inputs[(int)InputId.ColorR].Eval(context).Value;
            var ColorG = inputs[(int)InputId.ColorG].Eval(context).Value;
            var ColorB = inputs[(int)InputId.ColorB].Eval(context).Value;
            var ColorA = inputs[(int)InputId.ColorA].Eval(context).Value;
            var Color = new Color4(ColorR, ColorG, ColorB, ColorA);
            var Twist = inputs[(int)InputId.Twist].Eval(context).Value;
            var RangeStart = inputs[(int)InputId.RangeStart].Eval(context).Value;
            var RangeEnd = inputs[(int)InputId.RangeEnd].Eval(context).Value;
            var Range = new Vector2(RangeStart, RangeEnd);
            var Curve = inputs[(int)InputId.Curve].Eval(context).Dynamic;  // Needs to be checked for null!
            //<<< _params

            _curve = Curve as ICurveProvider;
            if( _curve == null) {            
                Logger.Warn(this, "Can't render because curve is not connected or invalid");
                return context;
            }


            if (_planeMesh == null || Changed) {
            
                var normal = new Vector3(0.0f, 0.0f, -1.0f);
                var color = Color;
                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)
                };
                
                _steps = (int)Steps;

                if (_steps < 2)
                    return context;
                
                _numControlPoints = _curve.GetPoints().Count();
                
                    
                
                // Setup vertex-structure
                const int attributesSize = 76;
                int numTriangles = (_steps-1) *2;
                int streamSize = numTriangles * 3 * attributesSize;

                if (_planeMesh == null || streamSize != _planeMesh.Vertices.Description.SizeInBytes) 
                {
                    Utilities.DisposeObj(ref _planeMesh);
                    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
                                           });
                        _planeMesh = new Mesh { InputElements = inputElements, Vertices = vertices, NumTriangles = numTriangles, AttributesSize = attributesSize };
                    }
                }
                if (_planeMesh.NumTriangles != numTriangles)
                {
                    _planeMesh.NumTriangles = numTriangles;
                }

                DataStream vertexStream;
                DataBox box = context.D3DDevice.ImmediateContext.MapSubresource(_planeMesh.Vertices, MapMode.WriteDiscard, MapFlags.None, out vertexStream);
                using (vertexStream)
                {
                    vertexStream.Position = 0;
    
                    float uShift = _paintedLength;
                    
                    var lastPointOnCurve = _curve.SampleAt(0); 
                    var lastP0 = new Vector4( lastPointOnCurve.X, lastPointOnCurve.Y + Width * lastPointOnCurve.W, lastPointOnCurve.Z, 1 );                    
                    var lastP1 = new Vector4( lastPointOnCurve.X, lastPointOnCurve.Y - Width * lastPointOnCurve.W, lastPointOnCurve.Z, 1 );                    
                    
                    for (int i = 0; i < _steps - 1; ++i) 
                    {
                        
                        var samplePos = i / (double)_steps * (_numControlPoints -2) * 3 ;
                        var nextPointOnCurve = _curve.SampleAt(samplePos);
                        var nextP0 = new Vector4( nextPointOnCurve.X, nextPointOnCurve.Y + Width * nextPointOnCurve.W, nextPointOnCurve.Z, 1 );                    
                        var nextP1 = new Vector4( nextPointOnCurve.X, nextPointOnCurve.Y - Width * nextPointOnCurve.W, nextPointOnCurve.Z, 1 );                                            
                        var u = (float)samplePos;
                    
                        // tri 1 vert 1
                        vertexStream.Write(lastP0);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write( new Vector2(u,0));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
    
                        // tri 1 vert 2
                        vertexStream.Write(nextP0);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(u, 1));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
    
                        // tri 1 vert 3
                        vertexStream.Write(lastP1);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(u, 0));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
    
                        // tri 2 vert 1
                        vertexStream.Write(nextP0);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(u, 1));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
    
                        // tri 2 vert 2
                        vertexStream.Write(nextP1);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(u, 1));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
    
                        // tri 2 vert 3
                        vertexStream.Write(lastP1);
                        vertexStream.Write(normal);
                        vertexStream.Write(color);
                        vertexStream.Write(new Vector2(u, 0));
                        vertexStream.Write(tangent);
                        vertexStream.Write(binormal);
                        
                        lastP0 = nextP0;
                        lastP1 = nextP1;
                    }
                    context.D3DDevice.ImmediateContext.UnmapSubresource(_planeMesh.Vertices, 0);
                }

                Changed = false;
            }

            context.Renderer.SetupEffect(context);
            context.Renderer.Render(_planeMesh, context);

            return context;
        }

        private Mesh _planeMesh;
    }
}