//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
//using Mathf;

namespace Framefield.Core.IDffab725a_7bdc_414d_b9df_5542aa69dd34
{
    public class Class_VJParameter : OperatorPart.Function, Framefield.Core.OperatorPartTraits.ITimeAccessor
    {
    
        //>>> _inputids
        private enum InputId
        {
            Mode = 0,
            SyncTrigger = 1,
            ResetTrigger = 2,
            StandardValue = 3,
            PulseAmount = 4,
            PulseScatter = 5,
            Regression = 6,
            Damping = 7,
            InputRangeMin = 8,
            InputRangeMax = 9,
            TriggerThreshold = 10,
            ShakeSpeed = 11
        }
        //<<< _inputids
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {
            //>>> _params
            var Mode = (int) inputs[(int)InputId.Mode].Eval(context).Value;
            var SyncTrigger = inputs[(int)InputId.SyncTrigger].Eval(context).Value;
            var ResetTrigger = inputs[(int)InputId.ResetTrigger].Eval(context).Value;
            var StandardValue = inputs[(int)InputId.StandardValue].Eval(context).Value;
            var PulseAmount = inputs[(int)InputId.PulseAmount].Eval(context).Value;
            var PulseScatter = inputs[(int)InputId.PulseScatter].Eval(context).Value;
            var Regression = inputs[(int)InputId.Regression].Eval(context).Value;
            var Damping = inputs[(int)InputId.Damping].Eval(context).Value;
            var InputRangeMin = inputs[(int)InputId.InputRangeMin].Eval(context).Value;
            var InputRangeMax = inputs[(int)InputId.InputRangeMax].Eval(context).Value;
            var InputRange = new Vector2(InputRangeMin, InputRangeMax);
            var TriggerThreshold = inputs[(int)InputId.TriggerThreshold].Eval(context).Value;
            var ShakeSpeed = inputs[(int)InputId.ShakeSpeed].Eval(context).Value;
            //<<< _params
            
            if( context.GlobalTime == _lastTime) {
                context.Value = _lastComputed;
                return context;
            }
            
            var triggerNormalized = (SyncTrigger - InputRangeMin) /  (InputRangeMax - InputRangeMin);
            var thresholdNormalized = (TriggerThreshold - InputRangeMin) /  (InputRangeMax - InputRangeMin);
            
            var hasBeenTriggered = triggerNormalized > _lastSyncTrigger && triggerNormalized >= thresholdNormalized;
            _lastSyncTrigger= triggerNormalized;
            
            if(hasBeenTriggered) {
                float scatter= (float) (_rand.NextDouble() - 0.5) * PulseScatter;
                //Logger.Info(this, "has been triggered");
            
                switch( (int)Mode) {
                // Fix Pulse
                case 0:
                    _value = StandardValue + PulseAmount + scatter;
                    break;
                
                // Intensity Pulse
                case 1:
                    _value = StandardValue + PulseAmount * (triggerNormalized + scatter);
                    break;
                
                // Additive Pulse
                case 2:
                    _value += PulseAmount * (triggerNormalized + scatter);
                    break;
                
                // Shake (Random)
                case 3:
                    _value += PulseAmount * (triggerNormalized + scatter);
                    break;
                    
                // Shake (Sin)
                case 4:
                    _value += PulseAmount * (triggerNormalized + scatter);
                    break;                    
                }            
            }
                        
            double dt = context.GlobalTime - _lastTime;
            _lastTime = context.GlobalTime;
           
            _value = (1 - Regression) * _value + (Regression * StandardValue);
            _damped = Damping * _damped + (1 - Damping) * _value; 
            
            
            // Random Shake
            if( (int)Mode == 3 ) {
                _lastComputed = (float)PerlinNoise( context.GlobalTime * ShakeSpeed) * _damped;
                                              
            }
            // Sin Shake
            else if( (int)Mode == 4) {
                _lastComputed = (float)Math.Sin( context.GlobalTime * ShakeSpeed) * _damped;            
            }
            else {
                _lastComputed = _damped;
            }
            
            context.Value = _lastComputed;
            return context;
        }
        
        
        #region Perlin noise
        public float Noise(int x, int seed) {
            int n = x + seed*137;
            n = (n<<13) ^ n;
            return (float)( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0); 
        }
        
        public float Lerp(float a, float b, float t) {
            return a + t * (b - a);
        }
        
        public float Fade(float t) {
            return t * t * t * (t * (t * 6 - 15) + 10);
        }
        
        private float PerlinNoise(float t) {
                                            
            int octaves = 2;
            float noiseSum =0.0f;
            float period = 2;
            float zoom = 1;
            float seed = 13;
            float rangeMax = 1;
            float rangeMin = -1;
            
            
            for(int a=0; a<octaves-1; a++) {
                float frequency = (float)Math.Pow(2,a); 
                float amplitude = (float)Math.Pow(period ,a);

                float v =  t * frequency/zoom  + seed*12.468f ;
                                               
                noiseSum += Lerp( Noise( (int)v  , (int)seed ),
                                  Noise( (int)v+1, (int)seed ),
                                  Fade( v - (float)Math.Floor(v) )
                            )* amplitude;

            }            
           return  (noiseSum+1f)*0.5f * (rangeMax - rangeMin) + rangeMin;
        }        
        #endregion

        // ---------------------------------
        
        float _lastComputed;
        float _value;
        float _damped;      
        float _lastSyncTrigger; 
        double _lastTime;
        Random _rand = new Random();
    }
}

