//>>> _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

namespace Framefield.Core.ID488920e6_ddff_4d3f_9c52_211756e3c540
{
    public class Class_PointBezierCurve : OperatorPart.Function, ICurveProvider
    {
        //>>> _inputids
        private enum InputId
        {
            Points = 0,
            U = 1
        }
        //<<< _inputids
        
        //>>> _outputids
        private enum OutputId
        {
            X = 0,
            Y = 1,
            Z = 2
        }
        //<<< _outputids
        
        public List<Vector4> GetPoints() {
            return _controlPoints;
        }
        
        public Vector4 SampleAt(double p) {
            return Vector4.Zero;
        }
        
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {
            //>>> _params
            var Points = inputs[(int)InputId.Points].Eval(context).Dynamic;  // Needs to be checked for null!
            var U = inputs[(int)InputId.U].Eval(context).Value;
            //<<< _params
            
            var somePointChanged = false;
            var pointConnections = inputs[(int)InputId.Points].Connections;
            
            _controlPoints = new List<Vector4>();
           
           foreach( var c in pointConnections) 
           {
                var p = c.Eval(context).Dynamic; 
                Vector4 pp = p;
                _controlPoints.Add( pp);
                somePointChanged = true;                    
            }
            
            _drawingPoints = GetDrawingPoints();
            
            if( U != _u) {
                _p = GetPointAtU(U);
                _u = U;            
            }
            
            switch( (int) outputIdx) {
                case (int)OutputId.X: context.Value= _p.X; break;
                case (int)OutputId.Y: context.Value= _p.Y; break;
                case (int)OutputId.Z: context.Value= _p.Z; break;
            }
                       
            return context;
        }

        private float _u;
        private Vector4 _p;
        private const float EPSILON = 0.0001f;
        private List<Vector4> _controlPoints;
        private Vector4[] _drawingPoints;
        private int _numPoints;
        const int SEGMENTS_PER_CURVE = 10;
        
        
        private Vector4 GetPointAtU(float u) 
        {
            int nCurves = _drawingPoints.Count() / SEGMENTS_PER_CURVE;
            if(nCurves ==0) {
                return Vector4.Zero;
                
            }
            else {
            
                var t = u / 3 * SEGMENTS_PER_CURVE;
                t= Utilities.Clamp( t, 0, _drawingPoints.Count() - 2);
                var n = (int)Math.Floor(t);
                
                return Vector4.Lerp( _drawingPoints[n], _drawingPoints[n+1], t - n); 
            }
        }
        
        Vector4 CalculateBezierPoint(float t, Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3)
        {
            float u = 1.0f - t;
            float tt = t*t;
            float uu = u*u;
            float uuu = uu * u;
            float ttt = tt * t;
            
            Vector4 p = uuu * p0; //first term
            p += 3.0f * uu * t * p1; //second term
            p += 3.0f * u * tt * p2; //third term
            p += ttt * p3; //fourth term
            
            return p;
        }  
        
        public Vector4[] GetDrawingPoints()
        {

            var curveCount = (_controlPoints.Count - 3)/3;
            var drawingPoints = new Vector4[(curveCount+1) * SEGMENTS_PER_CURVE +1];
            var index= 0;
            
            for(int i = 0; i < _controlPoints.Count - 3; i+=3)
            {
                Vector4 p0 = _controlPoints[i];
                Vector4 p1 = _controlPoints[i + 1];
                Vector4 p2 = _controlPoints[i + 2];
                Vector4 p3 = _controlPoints[i + 3];
        
                if(i == 0) //Only do this for the first endpoint.
                         //When i != 0, this coincides with the end
                         //point of the previous segment
                {
                    drawingPoints[index++] = CalculateBezierPoint(0, p0, p1, p2, p3);
                }    
        
                for(int j = 1; j <= SEGMENTS_PER_CURVE; j++)
                {
                    float t = j / (float) SEGMENTS_PER_CURVE;
                    drawingPoints[index++] = CalculateBezierPoint(t, p0, p1, p2, p3);
                }
            }
    
            return drawingPoints;
        }
    }    
    
}

