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

using System.Runtime.InteropServices;
using Framefield.Core;
using Framefield.Core.Rendering;
using SharpDX.DXGI;
using SharpDX.Direct3D;
using SharpDX.D3DCompiler;
using System.CodeDom.Compiler;
using Buffer = SharpDX.Direct3D11.Buffer;

namespace Framefield.Core.ID7c8523f5_5b67_4bca_a335_ba2476c40c17
{
    public class Class__renderFasterParticles : FXSourceCodeFunction, IFXSceneSourceCode
    {
        //structured buffer element type. size must be multiple of 4
        [StructLayout(LayoutKind.Explicit, Size = 16*4)]
        public struct ParticleStateLayout
        {
            public ParticleStateLayout(Vector3 emitPosition, Vector3 emitVelocity, float emitTime)
            {
                EmitPosition = emitPosition; 
                TrailIndex = 1;
                EmitVelocity = emitVelocity;
                EmitTime = emitTime;
                EmitIndex= 1;             
                __fillUp1= new Vector3();                
                __fillUp2= new Vector4();
            }
            [FieldOffset(    0)]    Vector3 EmitPosition;
            [FieldOffset( 3 *3)]    float TrailIndex;
            [FieldOffset( 4 *4)]    Vector3 EmitVelocity;
            [FieldOffset( 7 *4)]    float EmitTime;
            [FieldOffset( 8 *4)]    float EmitIndex;
            [FieldOffset( 9 *4)]    Vector3 __fillUp1;
            [FieldOffset( 12 *4)]   Vector4 __fillUp2;
        }

        //constant buffer element type. size must be multiple of 16
        [StructLayout(LayoutKind.Explicit, Size = 32*4)]
        public struct ParticleInitParametersBufferLayout
        {
            public ParticleInitParametersBufferLayout(int particlesStartIndex, 
                                                    int emitCount, 
                                                    float time, 
                                                      Vector3 emitPosition, 
                                                      Vector3 previousEmitPosition, 
                                                      int particleCount, 
                                                      Vector3 emitterSize, 
                                                      float limitPositions, 
                                                      Vector3 scatter, 
                                                      Vector2 emitVelocity, 
                                                      Vector2 emitAngleA, 
                                                      float previousEmitAngleA,
                                                      Vector2 emitAngleB, 
                                                      float previousEmitAngleB,
                                                      float emitStartTime, 
                                                      float emitPeriod,
                                                      int trailsLength,
                                                      int emitterShape)
            {
                StartIndex = particlesStartIndex;
                EmitCount = emitCount;
                Time = time;
                EmitPosition = emitPosition;
                PreviousEmitPosition = previousEmitPosition;
                ParticleCount = particleCount;
                EmitterSize = emitterSize;
                LimitPositions = limitPositions;
                Scatter = scatter;
                EmitVelocity = emitVelocity;
                EmitAngleA = emitAngleA;
                PreviousEmitAngleA = previousEmitAngleA;
                EmitAngleB = emitAngleB;
                PreviousEmitAngleB = previousEmitAngleB;
                EmitStartTime = emitStartTime;
                EmitPeriod = emitPeriod;
                TrailsLength = trailsLength;
                EmitterShape = emitterShape;
                
                BoxSubDivision = limitPositions>0.5? ComputeSubdivisionOfBox( emitterSize, (int)limitPositions)
                                                    : Vector3.Zero;
                //__dummy1=0;
            }
            
            [FieldOffset( 0 *4)]     int StartIndex;            
            [FieldOffset( 1 *4)]     int EmitCount;            
            [FieldOffset( 2 *4)]     float Time;
            [FieldOffset( 3 *4)]     int ParticleCount;
            
            [FieldOffset( 4 *4)]     Vector3 EmitPosition;
            [FieldOffset( 7 *4)]     float LimitPositions;                        

            [FieldOffset( 8 *4)]     Vector3 PreviousEmitPosition;
            [FieldOffset(11 *4)]     float EmitStartTime;

            [FieldOffset(12 *4)]     Vector3 EmitterSize;
            [FieldOffset(15 *4)]     float EmitPeriod;
            
            [FieldOffset(16 *4)]     Vector2 EmitVelocity;            
            [FieldOffset(18 *4)]     int TrailsLength;
            [FieldOffset(19 *4)]     int EmitterShape;
                        
            [FieldOffset(20 *4)]     Vector2 EmitAngleA;
            [FieldOffset(22 *4)]     Vector2 EmitAngleB; 

            [FieldOffset(24 *4)]     Vector3 BoxSubDivision;
            [FieldOffset(27 *4)]     float PreviousEmitAngleA;            

            [FieldOffset(28 *4)]     Vector3 Scatter;
            [FieldOffset(31 *4)]     float PreviousEmitAngleB;

            //[FieldOffset(32 *4)]     float __dummy1;
            
            

            
        }
        private Buffer _particleInitParametersBuffer;

        //constant buffer element type. size must be multiple of 16
        [StructLayout(LayoutKind.Explicit, Size = 208)] // was 208
        public struct ParticleUpdateParametersBufferLayout
        {
            public ParticleUpdateParametersBufferLayout(float deltaTime)
            {
                DeltaTime = deltaTime;
            }
            [FieldOffset(0)]
            float DeltaTime;
        }
        private Buffer _particleUpdateParametersBuffer;

        //>>> _inputids
        private enum InputId
        {
            InitCode = 0,
            RenderCode = 1,
            Texture = 2,
            ParticleCount = 3,
            LifeTime = 4,
            EmitRate = 5,
            EmitPositionX = 6,
            EmitPositionY = 7,
            EmitPositionZ = 8,
            ResetTrigger = 9,
            EmitterSizeX = 10,
            EmitterSizeY = 11,
            EmitterSizeZ = 12,
            LimitPositions = 13,
            ScatterX = 14,
            ScatterY = 15,
            ScatterZ = 16,
            EmitVelocityValue = 17,
            EmitAngleAAngle = 18,
            EmitAngleAScatter = 19,
            EmitAngleBAngle = 20,
            EmitAngleBScatter = 21,
            EmitFrom = 22,
            SizeValue = 23,
            SizeVariation = 24,
            StretchX = 25,
            StretchY = 26,
            StretchZ = 27,
            StretchByMotion = 28,
            ShrinkOverTimeAtBeginning = 29,
            ShrinkOverTimeAtEnd = 30,
            Orientation = 31,
            VelocityFriction = 32,
            GravityX = 33,
            GravityY = 34,
            GravityZ = 35,
            TurbulenceAmount = 36,
            TurbulenceTime = 37,
            TurbulenceScale = 38,
            TextureCellsColumns = 39,
            TextureCellsRows = 40,
            ColorStartR = 41,
            ColorStartG = 42,
            ColorStartB = 43,
            ColorStartA = 44,
            ColorVariationHue = 45,
            ColorVariationSaturation = 46,
            ColorVariationBrightness = 47,
            ColorVariationAlpha = 48,
            ColorEndR = 49,
            ColorEndG = 50,
            ColorEndB = 51,
            ColorEndA = 52,
            FadeWithAgeAtBeginning = 53,
            FadeWithAgeAtEnd = 54,
            FocusDistance = 55,
            FocusRange = 56,
            FadeTooCloseDistance = 57,
            FadeTooCloseRange = 58,
            ParametersAffect = 59,
            NoiseTextureRGB = 60,
            TrailsLength = 61,
            TrailsSpread = 62,
            EmitVelocityRandom = 63,
            TurbulenceVariation = 64,
            FadeTooFarDistance = 65,
            FadeTooFarRange = 66,
            TurbulenceFadeIn = 67
        }
        //<<< _inputids

        public override void Dispose()
        {
            Utilities.DisposeObj(ref _csInitParticles);
            Utilities.DisposeObj(ref _csUpdateParticles);
            Utilities.DisposeObj(ref _fxRenderParticles);
            Utilities.DisposeObj(ref _particleStateBuffer);
            Utilities.DisposeObj(ref _particleStateUAV);
            Utilities.DisposeObj(ref _particleStateSRV);
            base.Dispose();
        }

        bool _firstEval = true;
        private SamplerState _linearSamplerState;
        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            if (_firstEval)
            {
                for (int i = 0; i < NumCodes(); ++i)
                    Compile(i);
                _firstEval = false;
                Changed = true;
                    
                // create sampler state
                var samplerDesc = new SamplerStateDescription()
                                      {
                                          Filter = Filter.MinMagMipLinear,
                                          AddressU = TextureAddressMode.Wrap,
                                          AddressV = TextureAddressMode.Wrap,
                                          AddressW = TextureAddressMode.Wrap,
                                          //BorderColor = new Color4(0, 0, 0, 0),
                                          MipLodBias = 0,
                                          ComparisonFunction = Comparison.Never,
                                          MaximumAnisotropy = 16,
                                          MinimumLod = 0,
                                          MaximumLod = Single.MaxValue
                                      };
                Utilities.DisposeObj(ref _linearSamplerState);
                _linearSamplerState = new SamplerState(context.D3DDevice, samplerDesc);                

            }

            //>>> _params
            var InitCode = inputs[(int)InputId.InitCode].Eval(context).Text;
            var RenderCode = inputs[(int)InputId.RenderCode].Eval(context).Text;
            var Texture = inputs[(int)InputId.Texture].Eval(context).Image; // Needs to be checked for null!
            var ParticleCount = inputs[(int)InputId.ParticleCount].Eval(context).Value;
            var LifeTime = inputs[(int)InputId.LifeTime].Eval(context).Value;
            var EmitRate = inputs[(int)InputId.EmitRate].Eval(context).Value;
            var EmitPositionX = inputs[(int)InputId.EmitPositionX].Eval(context).Value;
            var EmitPositionY = inputs[(int)InputId.EmitPositionY].Eval(context).Value;
            var EmitPositionZ = inputs[(int)InputId.EmitPositionZ].Eval(context).Value;
            var EmitPosition = new Vector3(EmitPositionX, EmitPositionY, EmitPositionZ);
            var ResetTrigger = inputs[(int)InputId.ResetTrigger].Eval(context).Value;
            var EmitterSizeX = inputs[(int)InputId.EmitterSizeX].Eval(context).Value;
            var EmitterSizeY = inputs[(int)InputId.EmitterSizeY].Eval(context).Value;
            var EmitterSizeZ = inputs[(int)InputId.EmitterSizeZ].Eval(context).Value;
            var EmitterSize = new Vector3(EmitterSizeX, EmitterSizeY, EmitterSizeZ);
            var LimitPositions = inputs[(int)InputId.LimitPositions].Eval(context).Value;
            var ScatterX = inputs[(int)InputId.ScatterX].Eval(context).Value;
            var ScatterY = inputs[(int)InputId.ScatterY].Eval(context).Value;
            var ScatterZ = inputs[(int)InputId.ScatterZ].Eval(context).Value;
            var Scatter = new Vector3(ScatterX, ScatterY, ScatterZ);
            var EmitVelocityValue = inputs[(int)InputId.EmitVelocityValue].Eval(context).Value;
            var EmitVelocityRandom = inputs[(int)InputId.EmitVelocityRandom].Eval(context).Value;
            var EmitVelocity = new Vector2(EmitVelocityValue, EmitVelocityRandom);
            var EmitAngleAAngle = inputs[(int)InputId.EmitAngleAAngle].Eval(context).Value;
            var EmitAngleAScatter = inputs[(int)InputId.EmitAngleAScatter].Eval(context).Value;
            var EmitAngleA = new Vector2(EmitAngleAAngle, EmitAngleAScatter);
            var EmitAngleBAngle = inputs[(int)InputId.EmitAngleBAngle].Eval(context).Value;
            var EmitAngleBScatter = inputs[(int)InputId.EmitAngleBScatter].Eval(context).Value;
            var EmitAngleB = new Vector2(EmitAngleBAngle, EmitAngleBScatter);
            var EmitFrom = (int) inputs[(int)InputId.EmitFrom].Eval(context).Value;
            var SizeValue = inputs[(int)InputId.SizeValue].Eval(context).Value;
            var SizeVariation = inputs[(int)InputId.SizeVariation].Eval(context).Value;
            var Size = new Vector2(SizeValue, SizeVariation);
            var StretchX = inputs[(int)InputId.StretchX].Eval(context).Value;
            var StretchY = inputs[(int)InputId.StretchY].Eval(context).Value;
            var StretchZ = inputs[(int)InputId.StretchZ].Eval(context).Value;
            var Stretch = new Vector3(StretchX, StretchY, StretchZ);
            var StretchByMotion = inputs[(int)InputId.StretchByMotion].Eval(context).Value;
            var ShrinkOverTimeAtBeginning = inputs[(int)InputId.ShrinkOverTimeAtBeginning].Eval(context).Value;
            var ShrinkOverTimeAtEnd = inputs[(int)InputId.ShrinkOverTimeAtEnd].Eval(context).Value;
            var ShrinkOverTime = new Vector2(ShrinkOverTimeAtBeginning, ShrinkOverTimeAtEnd);
            var Orientation = (int) inputs[(int)InputId.Orientation].Eval(context).Value;
            var VelocityFriction = inputs[(int)InputId.VelocityFriction].Eval(context).Value;
            var GravityX = inputs[(int)InputId.GravityX].Eval(context).Value;
            var GravityY = inputs[(int)InputId.GravityY].Eval(context).Value;
            var GravityZ = inputs[(int)InputId.GravityZ].Eval(context).Value;
            var Gravity = new Vector3(GravityX, GravityY, GravityZ);
            var TurbulenceAmount = inputs[(int)InputId.TurbulenceAmount].Eval(context).Value;
            var TurbulenceTime = inputs[(int)InputId.TurbulenceTime].Eval(context).Value;
            var TurbulenceScale = inputs[(int)InputId.TurbulenceScale].Eval(context).Value;
            var TurbulenceVariation = inputs[(int)InputId.TurbulenceVariation].Eval(context).Value;
            var Turbulence = new Vector4(TurbulenceAmount, TurbulenceTime, TurbulenceScale, TurbulenceVariation);
            var TextureCellsColumns = inputs[(int)InputId.TextureCellsColumns].Eval(context).Value;
            var TextureCellsRows = inputs[(int)InputId.TextureCellsRows].Eval(context).Value;
            var TextureCells = new Vector2(TextureCellsColumns, TextureCellsRows);
            var ColorStartR = inputs[(int)InputId.ColorStartR].Eval(context).Value;
            var ColorStartG = inputs[(int)InputId.ColorStartG].Eval(context).Value;
            var ColorStartB = inputs[(int)InputId.ColorStartB].Eval(context).Value;
            var ColorStartA = inputs[(int)InputId.ColorStartA].Eval(context).Value;
            var ColorStart = new Color4(ColorStartR, ColorStartG, ColorStartB, ColorStartA);
            var ColorVariationHue = inputs[(int)InputId.ColorVariationHue].Eval(context).Value;
            var ColorVariationSaturation = inputs[(int)InputId.ColorVariationSaturation].Eval(context).Value;
            var ColorVariationBrightness = inputs[(int)InputId.ColorVariationBrightness].Eval(context).Value;
            var ColorVariationAlpha = inputs[(int)InputId.ColorVariationAlpha].Eval(context).Value;
            var ColorVariation = new Vector4(ColorVariationHue, ColorVariationSaturation, ColorVariationBrightness, ColorVariationAlpha);
            var ColorEndR = inputs[(int)InputId.ColorEndR].Eval(context).Value;
            var ColorEndG = inputs[(int)InputId.ColorEndG].Eval(context).Value;
            var ColorEndB = inputs[(int)InputId.ColorEndB].Eval(context).Value;
            var ColorEndA = inputs[(int)InputId.ColorEndA].Eval(context).Value;
            var ColorEnd = new Color4(ColorEndR, ColorEndG, ColorEndB, ColorEndA);
            var FadeWithAgeAtBeginning = inputs[(int)InputId.FadeWithAgeAtBeginning].Eval(context).Value;
            var FadeWithAgeAtEnd = inputs[(int)InputId.FadeWithAgeAtEnd].Eval(context).Value;
            var FadeWithAge = new Vector2(FadeWithAgeAtBeginning, FadeWithAgeAtEnd);
            var FocusDistance = inputs[(int)InputId.FocusDistance].Eval(context).Value;
            var FocusRange = inputs[(int)InputId.FocusRange].Eval(context).Value;
            var Focus = new Vector2(FocusDistance, FocusRange);
            var FadeTooCloseDistance = inputs[(int)InputId.FadeTooCloseDistance].Eval(context).Value;
            var FadeTooCloseRange = inputs[(int)InputId.FadeTooCloseRange].Eval(context).Value;
            var FadeTooClose = new Vector2(FadeTooCloseDistance, FadeTooCloseRange);
            var ParametersAffect = (int) inputs[(int)InputId.ParametersAffect].Eval(context).Value;
            var NoiseTextureRGB = inputs[(int)InputId.NoiseTextureRGB].Eval(context).Image; // Needs to be checked for null!
            var TrailsLength = inputs[(int)InputId.TrailsLength].Eval(context).Value;
            var TrailsSpread = inputs[(int)InputId.TrailsSpread].Eval(context).Value;
            var Trails = new Vector2(TrailsLength, TrailsSpread);
            var FadeTooFarDistance = inputs[(int)InputId.FadeTooFarDistance].Eval(context).Value;
            var FadeTooFarRange = inputs[(int)InputId.FadeTooFarRange].Eval(context).Value;
            var FadeTooFar = new Vector2(FadeTooFarDistance, FadeTooFarRange);
            var TurbulenceFadeIn = inputs[(int)InputId.TurbulenceFadeIn].Eval(context).Value;
            //<<< _params

            float lastTime = _time;
            _time = context.Time;
            float deltaTime = _time - lastTime;

            InitializeBuffer(context, (int)ParticleCount); 

            var deviceContext = context.D3DDevice.ImmediateContext;
            
            float emitPeriod = 1.0f/EmitRate;
            float emitStartTime = lastTime - (lastTime % emitPeriod); //- ParticleCount * emitPeriod;
            var numberOfParticlesToEmit = Math.Max(0, (int) ((_time - emitStartTime)*EmitRate )) * (int)( Math.Max(0, TrailsLength)+1);
            
            // Re-emit all particles...
            var reEmitAll = Math.Abs(ParametersAffect -0 ) < 0.5f || deltaTime < 0 || ResetTrigger > 0.5;
            if(reEmitAll) {
                _currentParticleIndex = 0;
                numberOfParticlesToEmit = (int)ParticleCount-1;
                emitStartTime -=  ParticleCount/(TrailsLength+1) * emitPeriod;
                //Logger.Info("reemit");                
            }
            
            //numberOfParticlesToEmit *= ;

            
            //emitStartTime = _time - numberOfParticlesToEmit * emitPeriod;
            
            //float lastParticleEmitTime = _time - ParticleCount - (lastTime % emitPeriod);
            //float emitStartTime = lastParticleEmitTime + emitPeriod;
            
//            Vector3 emitCenterDelta = (EmitCenter - _previousEmitCenter);
//            if (emitTime < context.Time)
//            {
                //Logger.Info("n:{0}" , emitCenterDelta);
//            }

            if(Texture == null) {
                Logger.Info(this, "Missing Particle-Texture");
                return context;
            }
            if(NoiseTextureRGB == null) {
                Logger.Info(this, "Missing RGBNoise-Texture");
                return context;
            }

            using (var imageSRV = new ShaderResourceView(context.D3DDevice, Texture))
            using (var noiseTextureSRV = new ShaderResourceView(context.D3DDevice, NoiseTextureRGB))
            {
               if(numberOfParticlesToEmit > 0) 
               {
                    //Logger.Info("EmitCount: {0}  T:{1} StartTime:{2} dt:{3}, @{4}", numberOfParticlesToEmit, _time, emitStartTime, emitPeriod, _currentParticleIndex);
                    int particleEmitCountForFrame = numberOfParticlesToEmit;
                        
                    //int particleEndIndex = (_currentParticleIndex + particleEmitCountForFrame) % _particleCount;
                    var param = new ParticleInitParametersBufferLayout(
                                        _currentParticleIndex, 
                                        numberOfParticlesToEmit, 
                                        _time, 
                                        EmitPosition, 
                                        _previousEmitPosition,
                                        _particleCount, 
                                        EmitterSize, 
                                        LimitPositions, 
                                        Scatter, 
                                        EmitVelocity, 
                                        EmitAngleA, 
                                        _previousEmitAngleA,
                                        EmitAngleB, 
                                        _previousEmitAngleB,
                                        emitStartTime, 
                                        emitPeriod,
                                        (int)TrailsLength,
                                        (int)EmitFrom 
                    );
                    BaseRenderer.SetupConstBufferForCS<ParticleInitParametersBufferLayout>(context, param, ref _particleInitParametersBuffer, 0);
        
                    _currentParticleIndex = (_currentParticleIndex + numberOfParticlesToEmit) % _particleCount;
                            
                    deviceContext.ComputeShader.Set(_csInitParticles);
                    deviceContext.ComputeShader.SetUnorderedAccessView(0, _particleStateUAV);
                    deviceContext.ComputeShader.SetShaderResource(0, noiseTextureSRV);
                    deviceContext.ComputeShader.SetConstantBuffer(0, _particleInitParametersBuffer);                    
                    deviceContext.ComputeShader.SetSampler(0, _linearSamplerState);
        
                    deviceContext.Dispatch(_particleCount/512+1, 1, 1);
        
                    deviceContext.ComputeShader.SetShaderResource(0, null);
                    deviceContext.ComputeShader.SetConstantBuffer(0, null);
                    deviceContext.ComputeShader.SetSampler(0, null);
        
                    _previousEmitPosition = EmitPosition;
                    _previousEmitAngleA = EmitAngleAAngle;
                    _previousEmitAngleB = EmitAngleBAngle;
                }
    
                // Particle updating
                deviceContext.ComputeShader.SetUnorderedAccessView(0, null);
                deviceContext.ComputeShader.SetConstantBuffer(1, null);
            

                // Particle rendering
                try
                {                
                    var lifeTime = LifeTime <=0 ?  ParticleCount/(EmitRate* (TrailsLength+1)) : LifeTime;

                    // Note: Emit paramaters are passed with ConstantBuffer above                    
                    _fxRenderParticles.GetVariableByName("ParticleStates").AsShaderResource().SetResource(_particleStateSRV);

                    _fxRenderParticles.GetVariableByName("NoiseTextureRGB").AsShaderResource().SetResource(noiseTextureSRV);
                    _fxRenderParticles.GetVariableByName("Texture").AsShaderResource().SetResource(imageSRV);
                    _fxRenderParticles.GetVariableByName("Time").AsScalar().Set((float)_time);
                    _fxRenderParticles.GetVariableByName("LifeTime").AsScalar().Set((float)lifeTime);
                    _fxRenderParticles.GetVariableByName("EmitRate").AsScalar().Set(EmitRate);

                    _fxRenderParticles.GetVariableByName("ShrinkOverTime").AsVector().Set(ShrinkOverTime);
                    _fxRenderParticles.GetVariableByName("Size").AsVector().Set(Size);
                    _fxRenderParticles.GetVariableByName("Stretch").AsVector().Set(Stretch);
                    
                    _fxRenderParticles.GetVariableByName("Orientation").AsScalar().Set(Orientation);
                    _fxRenderParticles.GetVariableByName("StretchByMotion").AsScalar().Set(StretchByMotion);
                    _fxRenderParticles.GetVariableByName("VelocityFriction").AsScalar().Set(VelocityFriction);
                    _fxRenderParticles.GetVariableByName("Gravity").AsVector().Set(Gravity);
                    _fxRenderParticles.GetVariableByName("Turbulence").AsVector().Set(Turbulence);
                    _fxRenderParticles.GetVariableByName("TextureCells").AsVector().Set( new Vector2( (float)Math.Round(TextureCells.X), (float)Math.Round(TextureCells.Y)) );
                    _fxRenderParticles.GetVariableByName("ColorStart").AsVector().Set(ColorStart);
                    _fxRenderParticles.GetVariableByName("ColorEnd").AsVector().Set(ColorEnd);
                    _fxRenderParticles.GetVariableByName("ColorVariation").AsVector().Set(ColorVariation);
                    _fxRenderParticles.GetVariableByName("FadeWithAge").AsVector().Set(FadeWithAge);
                    _fxRenderParticles.GetVariableByName("Focus").AsVector().Set(Focus);
                    _fxRenderParticles.GetVariableByName("FadeTooClose").AsVector().Set(FadeTooClose);
                    _fxRenderParticles.GetVariableByName("FadeTooFar").AsVector().Set(FadeTooFar);
                    _fxRenderParticles.GetVariableByName("Trails").AsVector().Set(Trails);
                    _fxRenderParticles.GetVariableByName("TurbulenceFadeIn").AsScalar().Set(TurbulenceFadeIn);
                    //SetScalar("TurbulenceFadeIn",TurbulenceFadeIn);
                    
                    var previousEffect = context.Effect;
                    context.Effect = _fxRenderParticles;
                    context.Renderer.SetupEffect(context);

                    if (context.DepthStencilView != null)
                        deviceContext.OutputMerger.SetTargets(context.DepthStencilView, context.RenderTargetView);
                    else
                        deviceContext.OutputMerger.SetTargets(context.RenderTargetView);

                    if (context.BlendState != null) {
                        deviceContext.OutputMerger.BlendState = context.BlendState;
                        deviceContext.OutputMerger.BlendFactor = context.BlendFactor;
                    }

                    if (context.DepthStencilState != null) {
                        deviceContext.OutputMerger.DepthStencilState = context.DepthStencilState;
                    }

                    if (context.RasterizerState != null) {
                        deviceContext.Rasterizer.State = context.RasterizerState;
                    }

                    deviceContext.Rasterizer.SetViewport(context.Viewport);
                    deviceContext.InputAssembler.InputLayout = context.InputLayout;
                    deviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
                    deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(null, 0, 0));

                    var technique = context.Effect.GetTechniqueByIndex(0);
                    for (int i = 0; i < technique.Description.PassCount; ++i) {
                        technique.GetPassByIndex(i).Apply(deviceContext);
                        deviceContext.Draw(_particleCount, 0);
                    }

                    // remove target views that they are no longer bound as output and can be used also as input
                    DepthStencilView dsv = null;
                    RenderTargetView rtv = null;
                    deviceContext.OutputMerger.SetTargets(dsv, rtv);
                    deviceContext.VertexShader.SetShaderResource(0, null);
                    deviceContext.PixelShader.SetShaderResource(0, null);
                    context.Effect = previousEffect;
                }
                catch (Exception exception)
                {
                    Logger.Error(this,"render error: {0}", exception.Message);
                }
            }

            Changed = false;
            return context;
        }

        void InitializeBuffer(OperatorPartContext context, int newMaxNumParticles)
        {
            newMaxNumParticles = Math.Max(1, newMaxNumParticles); //at least we need to have 512 particles

            if (_particleStateBuffer == null || _particleCount != newMaxNumParticles)
            {
          
                Utilities.DisposeObj(ref _particleStateBuffer);
                using (var data = new DataStream(Marshal.SizeOf(typeof(ParticleStateLayout))*newMaxNumParticles, true, true))
                {
                    for (int i = 0; i < newMaxNumParticles; ++i)
                        data.Write(new ParticleStateLayout(new Vector3(0), new Vector3(0), 0.0f));
                    data.Position = 0;

                    var bufferDesc = new BufferDescription
                                         {
                                             Usage = ResourceUsage.Default,
                                             SizeInBytes = Marshal.SizeOf(typeof(ParticleStateLayout))*newMaxNumParticles,
                                             StructureByteStride = Marshal.SizeOf(typeof(ParticleStateLayout)),
                                             BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
                                             OptionFlags = ResourceOptionFlags.BufferStructured
                                         };
                    _particleStateBuffer = new Buffer(context.D3DDevice, data, bufferDesc);
                }
                _particleCount = newMaxNumParticles;

                var uavDesc = new UnorderedAccessViewDescription
                                  {
                                      Format = Format.Unknown, 
                                      Dimension = UnorderedAccessViewDimension.Buffer,
                                      Buffer = new UnorderedAccessViewDescription.BufferResource
                                                   {
                                                       FirstElement = 0,
                                                       ElementCount = _particleCount,
                                                       Flags = UnorderedAccessViewBufferFlags.None
                                                   }
                                  };

                Utilities.DisposeObj(ref _particleStateUAV);
                _particleStateUAV = new UnorderedAccessView(context.D3DDevice, _particleStateBuffer, uavDesc);

                Utilities.DisposeObj(ref _particleStateSRV);
                _particleStateSRV = new ShaderResourceView(context.D3DDevice, _particleStateBuffer);

            }
        }

        public override int NumCodes()
        {
            return 2;
        }

        public override CompilerErrorCollection Compile(int codeIdx)
        {
            var errors = new CompilerErrorCollection();
            try
            {
                if (codeIdx == (int)InputId.InitCode)
                {
                    Utilities.DisposeObj(ref _csInitParticles);
                    var t = GetCode(codeIdx);
                    using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "CSInitParticles", "cs_5_0", ShaderFlags.Debug))
                        _csInitParticles = new ComputeShader(D3DDevice.Device, bytecode);

                    Utilities.DisposeObj(ref _csUpdateParticles);
//                    using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "CSUpdateParticles", "cs_5_0", ShaderFlags.Debug))
//                        _csUpdateParticles = new ComputeShader(D3DDevice.Device, bytecode);
                }
                else
                {
                    Utilities.DisposeObj(ref _fxRenderParticles);
                    using (var bytecode = ShaderBytecode.Compile(GetCode(codeIdx), "fx_5_0", ShaderFlags.Debug, EffectFlags.None, null, null))
                        _fxRenderParticles = new Effect(D3DDevice.Device, bytecode);
                }
            }
            catch (SharpDX.CompilationException ex)
            {
                errors = ErrorsFromString(ex.Message);
                Logger.Error(this,"Fx compile error: {0}", ex.Message);
            }
            return errors;
        }
        
        private static Vector3 ComputeSubdivisionOfBox( Vector3 VolumeSize, int Count) 
        {
            var volume = Math.Abs(VolumeSize.X) * Math.Abs(VolumeSize.Y) * Math.Abs(VolumeSize.Z);            
            var s1= new Vector3(Math.Abs(VolumeSize.X), Math.Abs(VolumeSize.Y), Math.Abs(VolumeSize.Z));
            var a= s1.Y/s1.X;
            var b= s1.Z/s1.X;
            
            var xx = (float)Math.Pow( 1/ (a*b), 1.0/3.0);
            var s2 = s1 * (xx/s1.X);
                                        
            var edgeCount= Math.Pow(Count,1.0/3.0);
            
            var itemsX= (float)Math.Max(1, Math.Round(edgeCount * s2.X));
            var itemsY= (float)Math.Max(1, Math.Round(edgeCount * s2.Y));
            var itemsZ= (float)Math.Max(1, Math.Round(edgeCount * s2.Z));

            if( itemsZ> itemsX && itemsZ> itemsY) {
                itemsZ= Count/itemsX/itemsY;
            }
            else if( itemsX > itemsY && itemsX > itemsZ) {
                itemsX= Count/itemsY/itemsZ;
            }
            else {
                itemsY= Count/itemsX/itemsZ;
            }

            //var x = (int)(i % itemsX)/ itemsX;
            //var y = (int)((i/itemsX) % itemsY) / itemsY;
            //var z = (int)((i/itemsX/itemsY)) / itemsZ;
                                                                    
            //t = new Vector3((float) (x- (itemsX > 1 ? 0.5 : 0)),
            //                (float) (y- (itemsY > 1 ? 0.5 : 0)),
            //               (float) (z- (itemsZ > 1 ? 0.5 : 0))) * Stretch;
            //Logger.Info("{0} {1} {2} ",itemsX, itemsY, itemsZ);
            return new Vector3(itemsX, itemsY, itemsZ);
                                            
        
        }

        ComputeShader _csInitParticles;
        ComputeShader _csUpdateParticles;
        Effect _fxRenderParticles;
        Buffer _particleStateBuffer;
        UnorderedAccessView _particleStateUAV;
        ShaderResourceView _particleStateSRV;
        int _particleCount;
        int _currentParticleIndex;
        Vector3 _previousEmitPosition;
        float _previousEmitAngleA= 0;
        float _previousEmitAngleB = 0;
        //Vector3 _previousEmitDirection;

        float _time;
    }
}


