//>>> _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.ID89c11108_1fee_49f5_999e_0f521679b1d5
{
    public class Class_PointOnCatRomCurve : OperatorPart.Function, ICurveProvider
    {
        //>>> _inputids
        private enum InputId
        {
            Points = 0,
            U = 1,
            Resolution = 2
        }
        //<<< _inputids
        
        //>>> _outputids
        private enum OutputId
        {
            X = 0,
            Y = 1,
            Z = 2,
            This = 3
        }
        //<<< _outputids
        
        public List<Vector4> GetPoints() 
        {
            return _controlPoints.ToList();
        }
        
        
        
        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;
            var Resolution = inputs[(int)InputId.Resolution].Eval(context).Value;
            //<<< _params
                        
            var needsRebuild = false;
            var pointConnections = inputs[(int)InputId.Points].Connections;
            
            if( _resolution != (int) Resolution) {
                _resolution = (int)Resolution;
                needsRebuild = true;
            }

            // Reinit List (1st and last Points are reused)
            if( _controlPoints == null || _controlPoints.Count() != pointConnections.Count() + 2)  {
                _controlPoints = new Vector4[pointConnections.Count() + 2];
                needsRebuild = true;
            }
            
            
            if(Changed) 
            {
                //var newControlPoints = new Vector4[ pointConnections.Count ];
                
                for(var i=0; i < pointConnections.Count ; ++i) {
                    var c = pointConnections[i];
                    var p = c.Eval(context).Dynamic; 
                    Vector4 pp = p;
                    
                    if( pp != _controlPoints[i+1] )
                    {
                        _controlPoints[i+1]=pp;
                        if(i ==0 ) {
                            _controlPoints[0]=pp;
                        }
                        else if(i == pointConnections.Count - 1) {
                            _controlPoints[pointConnections.Count]=pp;
                        }
                        needsRebuild = true;
                    }                    
                }
                
                if(needsRebuild) {
                    UpdateDrawingPointsWithCatmullRom( _controlPoints, out _drawingPoints, _resolution );
                }
                                    
            
                if( U != _u || needsRebuild) {                
                    _p = SampleAt( U );
                    _u = U;
                }
                Changed= false;
            }
            
            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;
                case (int)OutputId.This: context.Dynamic= this; break;
            }
                       
            return context;
        }

        private float _u;
        private Vector4 _p = Vector4.Zero;
        private int _resolution = 1000;
        private const float EPSILON = 0.0001f;
        private int _numPoints;
        private Vector4[] _controlPoints;
        private Vector4[] _drawingPoints;
        const int SEGMENTS_PER_CURVE = 10;
        
        public Vector4 SampleAt(double u) 
        {
            if(_drawingPoints == null || _drawingPoints.Count() <2)
                return Vector4.Zero;
                
            u *= _resolution;
            u  =  (float)Utilities.Clamp(u  , 0, _drawingPoints.Count()-2);
            var i = (int)u;
            
            return  Vector4.Lerp( _drawingPoints[i], _drawingPoints[i+1], (float)(u-i));
        }
        
        /// <summary>
        /// Takes an array of input coordinates used to define a Catmull-Rom spline, and then
        /// samples the resulting spline according to the specified sample count (per span),
        /// populating the output array with the newly sampled coordinates. The returned boolean
        /// indicates whether the operation was successful (true) or not (false).
        /// NOTE: The first and last points specified are used to describe curvature and will be dropped
        /// from the resulting spline. Duplicate them if you wish to include them in the curve.
        /// </summary>
        public bool UpdateDrawingPointsWithCatmullRom(Vector4[] inCoordinates, out Vector4[] outCoordinates, int samples)
        {
        	if (inCoordinates.Length < 4)
        	{
        		outCoordinates = null;
        		return false;
        	}
         
        	List<Vector4> results = new List<Vector4>();
         
        	for (int n = 1; n < inCoordinates.Length - 2; n++)
        		for (int i = 0; i < samples; i++)
        			results.Add(PointOnCurve(inCoordinates[n - 1], inCoordinates[n], inCoordinates[n + 1], inCoordinates[n + 2], (1f / samples) * i ));
         
        	results.Add(inCoordinates[inCoordinates.Length - 2]);
         
        	outCoordinates = results.ToArray();
        	return true;
        }
         
        /// <summary>
        /// Return a point on the curve between P1 and P2 with P0 and P4 describing curvature, at
        /// the normalized distance t.
        /// </summary>
         
        public static Vector4 PointOnCurve(Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3, float t)
        {
        	Vector4 result = Vector4.One;
         
        	float t0 = ((-t + 2f) * t - 1f) * t * 0.5f;
        	float t1 = (((3f * t - 5f) * t) * t + 2f) * 0.5f;
        	float t2 = ((-3f * t + 4f) * t + 1f) * t * 0.5f;
        	float t3 = ((t - 1f) * t * t) * 0.5f;
          	result = p0 * t0 + p1 * t1 + p2 * t2 + p3 * t3;
          	         
        	return result;
        }        
    }    
    
}

