//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using Framefield.Core.OperatorPartTraits;
using Framefield.Core.Rendering;
using Buffer = SharpDX.Direct3D11.Buffer;
using SharpDX.Direct3D;

namespace Framefield.Core.IDe4a7cffe_2f87_4754_82fb_414a59eff411
{
    public class Class_SplineDistort__3_ : FXSourceCodeFunction, IFXSceneSourceCode, IMeshSupplier
    {
        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            Scene = 1,
            Shift = 2,
            StraightRegionStart = 3,
            StraightRegionEnd = 4,
            StraightRegionBlend = 5,
            RibbonWidth = 6,
            StraightSpacing = 7,
            SpinRegionStart = 8,
            SpinRegionEnd = 9,
            SpinRegionBlend = 10,
            SpinPeriod = 11,
            SpinPhase = 12,
            SpinRibbonOffset = 13,
            RibbonShift = 14,
            SinRegionStart = 15,
            SinRegionEnd = 16,
            SinRegionBlend = 17,
            PeriodAX = 18,
            PeriodAY = 19,
            PeriodAZ = 20,
            AmplitudeAX = 21,
            AmplitudeAY = 22,
            AmplitudeAZ = 23,
            PhaseAX = 24,
            PhaseAY = 25,
            PhaseAZ = 26,
            PeriodBX = 27,
            PeriodBY = 28,
            PeriodBZ = 29,
            AmplitudeBX = 30,
            AmplitudeBY = 31,
            AmplitudeBZ = 32,
            PhaseBX = 33,
            PhaseBY = 34,
            PhaseBZ = 35,
            CircleRegionStart = 36,
            CircleRegionEnd = 37,
            CircleRegionBlend = 38,
            CircleRadius = 39,
            CircleCircumfence = 40,
            CircleSpin = 41,
            StraightOffset = 42,
            StraightDirectionX = 43,
            StraightDirectionY = 44,
            StraightDirectionZ = 45,
            SinSplitA = 46,
            SinSplitB = 47
        }
        //<<< _inputids

        public Class_SplineDistort__3_()
        {
            _meshCollector = new MeshCollector(this);
        }

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

        public void AddMeshesTo(ICollection<Mesh> meshes)
        {
            UpdateMesh(new OperatorPartContext(), OperatorPart.Connections);
            if (_mesh != null)
                meshes.Add(_mesh);
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            try
            {
                UpdateMesh(context, inputs);

                if (_mesh.Vertices != null)
                {
                    // render stream output to screen or whatever target is set
                    context.Renderer.SetupEffect(context);
                    context.Renderer.Render(_mesh, context);
                }
            }
            catch (Exception exception)
            {
                Logger.Error(this, "Load Effect error: {0}", exception.Message);
            }

            return context;
        }

        bool _firstEval = true;
        private void UpdateMesh(OperatorPartContext context, List<OperatorPart> inputs)
        {
            if (_firstEval)
            {
                for (int i = 0; i < NumCodes(); ++i)
                    Compile(i);
                _firstEval = false;
                Changed = true;
            }

            if (!Changed && _mesh.Vertices != null)
                return;

            //>>> _params
            var Code = inputs[(int)InputId.Code].Eval(context).Text;
            var Scene = inputs[(int)InputId.Scene];
            var Shift = inputs[(int)InputId.Shift].Eval(context).Value;
            var StraightRegionStart = inputs[(int)InputId.StraightRegionStart].Eval(context).Value;
            var StraightRegionEnd = inputs[(int)InputId.StraightRegionEnd].Eval(context).Value;
            var StraightRegionBlend = inputs[(int)InputId.StraightRegionBlend].Eval(context).Value;
            var StraightRegion = new Vector3(StraightRegionStart, StraightRegionEnd, StraightRegionBlend);
            var RibbonWidth = inputs[(int)InputId.RibbonWidth].Eval(context).Value;
            var StraightSpacing = inputs[(int)InputId.StraightSpacing].Eval(context).Value;
            var SpinRegionStart = inputs[(int)InputId.SpinRegionStart].Eval(context).Value;
            var SpinRegionEnd = inputs[(int)InputId.SpinRegionEnd].Eval(context).Value;
            var SpinRegionBlend = inputs[(int)InputId.SpinRegionBlend].Eval(context).Value;
            var SpinRegion = new Vector3(SpinRegionStart, SpinRegionEnd, SpinRegionBlend);
            var SpinPeriod = inputs[(int)InputId.SpinPeriod].Eval(context).Value;
            var SpinPhase = inputs[(int)InputId.SpinPhase].Eval(context).Value;
            var SpinRibbonOffset = inputs[(int)InputId.SpinRibbonOffset].Eval(context).Value;
            var Spin = new Vector3(SpinPeriod, SpinPhase, SpinRibbonOffset);
            var RibbonShift = inputs[(int)InputId.RibbonShift].Eval(context).Value;
            var SinRegionStart = inputs[(int)InputId.SinRegionStart].Eval(context).Value;
            var SinRegionEnd = inputs[(int)InputId.SinRegionEnd].Eval(context).Value;
            var SinRegionBlend = inputs[(int)InputId.SinRegionBlend].Eval(context).Value;
            var SinRegion = new Vector3(SinRegionStart, SinRegionEnd, SinRegionBlend);
            var PeriodAX = inputs[(int)InputId.PeriodAX].Eval(context).Value;
            var PeriodAY = inputs[(int)InputId.PeriodAY].Eval(context).Value;
            var PeriodAZ = inputs[(int)InputId.PeriodAZ].Eval(context).Value;
            var PeriodA = new Vector3(PeriodAX, PeriodAY, PeriodAZ);
            var AmplitudeAX = inputs[(int)InputId.AmplitudeAX].Eval(context).Value;
            var AmplitudeAY = inputs[(int)InputId.AmplitudeAY].Eval(context).Value;
            var AmplitudeAZ = inputs[(int)InputId.AmplitudeAZ].Eval(context).Value;
            var AmplitudeA = new Vector3(AmplitudeAX, AmplitudeAY, AmplitudeAZ);
            var PhaseAX = inputs[(int)InputId.PhaseAX].Eval(context).Value;
            var PhaseAY = inputs[(int)InputId.PhaseAY].Eval(context).Value;
            var PhaseAZ = inputs[(int)InputId.PhaseAZ].Eval(context).Value;
            var PhaseA = new Vector3(PhaseAX, PhaseAY, PhaseAZ);
            var PeriodBX = inputs[(int)InputId.PeriodBX].Eval(context).Value;
            var PeriodBY = inputs[(int)InputId.PeriodBY].Eval(context).Value;
            var PeriodBZ = inputs[(int)InputId.PeriodBZ].Eval(context).Value;
            var PeriodB = new Vector3(PeriodBX, PeriodBY, PeriodBZ);
            var AmplitudeBX = inputs[(int)InputId.AmplitudeBX].Eval(context).Value;
            var AmplitudeBY = inputs[(int)InputId.AmplitudeBY].Eval(context).Value;
            var AmplitudeBZ = inputs[(int)InputId.AmplitudeBZ].Eval(context).Value;
            var AmplitudeB = new Vector3(AmplitudeBX, AmplitudeBY, AmplitudeBZ);
            var PhaseBX = inputs[(int)InputId.PhaseBX].Eval(context).Value;
            var PhaseBY = inputs[(int)InputId.PhaseBY].Eval(context).Value;
            var PhaseBZ = inputs[(int)InputId.PhaseBZ].Eval(context).Value;
            var PhaseB = new Vector3(PhaseBX, PhaseBY, PhaseBZ);
            var CircleRegionStart = inputs[(int)InputId.CircleRegionStart].Eval(context).Value;
            var CircleRegionEnd = inputs[(int)InputId.CircleRegionEnd].Eval(context).Value;
            var CircleRegionBlend = inputs[(int)InputId.CircleRegionBlend].Eval(context).Value;
            var CircleRegion = new Vector3(CircleRegionStart, CircleRegionEnd, CircleRegionBlend);
            var CircleRadius = inputs[(int)InputId.CircleRadius].Eval(context).Value;
            var CircleCircumfence = inputs[(int)InputId.CircleCircumfence].Eval(context).Value;
            var CircleSpin = inputs[(int)InputId.CircleSpin].Eval(context).Value;
            var StraightOffset = inputs[(int)InputId.StraightOffset].Eval(context).Value;
            var StraightDirectionX = inputs[(int)InputId.StraightDirectionX].Eval(context).Value;
            var StraightDirectionY = inputs[(int)InputId.StraightDirectionY].Eval(context).Value;
            var StraightDirectionZ = inputs[(int)InputId.StraightDirectionZ].Eval(context).Value;
            var StraightDirection = new Vector3(StraightDirectionX, StraightDirectionY, StraightDirectionZ);
            var SinSplitA = inputs[(int)InputId.SinSplitA].Eval(context).Value;
            var SinSplitB = inputs[(int)InputId.SinSplitB].Eval(context).Value;
            var SinSplit = new Vector2(SinSplitA, SinSplitB);
            //<<< _params

            if (Scene.Func.Changed || _mesh.Vertices == null)
            {
                //Logger.Info(this, "collect meshes");
                _meshCollector.Collect(Scene);
                Scene.Func.Changed = false;
            }

            var inputMesh = _meshCollector.FirstMeshOrDefault;
            if (inputMesh == null)
                return;

            _mesh.AttributesSize = inputMesh.AttributesSize;
            _mesh.NumTriangles = inputMesh.NumTriangles;
            _mesh.InputElements = inputMesh.InputElements;
            if (_mesh.Vertices == null || _mesh.Vertices.Description.SizeInBytes != inputMesh.Vertices.Description.SizeInBytes)
            {
                if (_mesh.Vertices != null)
                    _mesh.Vertices.Dispose();

                _mesh.Vertices = new Buffer(D3DDevice.Device, new BufferDescription()
                                                                    {
                                                                        BindFlags = BindFlags.StreamOutput | BindFlags.VertexBuffer,
                                                                        CpuAccessFlags = CpuAccessFlags.None,
                                                                        OptionFlags = ResourceOptionFlags.None,
                                                                        SizeInBytes = _mesh.NumTriangles*inputMesh.AttributesSize*3,
                                                                        Usage = ResourceUsage.Default
                                                                    });
            }
            //var noiseTextureView = new ShaderResourceView(context.D3DDevice, RGBANoise);
            //_effect.GetVariableByName("RGBANoise").AsShaderResource().SetResource(noiseTextureView);
            _usedViewport= context.Viewport;
            
            //>>> _setup
            {
                _effect.GetVariableByName("RenderTargetSize").AsVector().Set(new Vector2(_usedViewport.Width, _usedViewport.Height));
                _effect.GetVariableByName("Shift").AsScalar().Set(Shift);
                _effect.GetVariableByName("StraightRegion").AsVector().Set(new Vector3(StraightRegionStart, StraightRegionEnd, StraightRegionBlend));
                _effect.GetVariableByName("RibbonWidth").AsScalar().Set(RibbonWidth);
                _effect.GetVariableByName("StraightSpacing").AsScalar().Set(StraightSpacing);
                _effect.GetVariableByName("SpinRegion").AsVector().Set(new Vector3(SpinRegionStart, SpinRegionEnd, SpinRegionBlend));
                _effect.GetVariableByName("Spin").AsVector().Set(new Vector3(SpinPeriod, SpinPhase, SpinRibbonOffset));
                _effect.GetVariableByName("RibbonShift").AsScalar().Set(RibbonShift);
                _effect.GetVariableByName("SinRegion").AsVector().Set(new Vector3(SinRegionStart, SinRegionEnd, SinRegionBlend));
                _effect.GetVariableByName("PeriodA").AsVector().Set(new Vector3(PeriodAX, PeriodAY, PeriodAZ));
                _effect.GetVariableByName("AmplitudeA").AsVector().Set(new Vector3(AmplitudeAX, AmplitudeAY, AmplitudeAZ));
                _effect.GetVariableByName("PhaseA").AsVector().Set(new Vector3(PhaseAX, PhaseAY, PhaseAZ));
                _effect.GetVariableByName("PeriodB").AsVector().Set(new Vector3(PeriodBX, PeriodBY, PeriodBZ));
                _effect.GetVariableByName("AmplitudeB").AsVector().Set(new Vector3(AmplitudeBX, AmplitudeBY, AmplitudeBZ));
                _effect.GetVariableByName("PhaseB").AsVector().Set(new Vector3(PhaseBX, PhaseBY, PhaseBZ));
                _effect.GetVariableByName("CircleRegion").AsVector().Set(new Vector3(CircleRegionStart, CircleRegionEnd, CircleRegionBlend));
                _effect.GetVariableByName("CircleRadius").AsScalar().Set(CircleRadius);
                _effect.GetVariableByName("CircleCircumfence").AsScalar().Set(CircleCircumfence);
                _effect.GetVariableByName("CircleSpin").AsScalar().Set(CircleSpin);
                _effect.GetVariableByName("StraightOffset").AsScalar().Set(StraightOffset);
                _effect.GetVariableByName("StraightDirection").AsVector().Set(new Vector3(StraightDirectionX, StraightDirectionY, StraightDirectionZ));
                _effect.GetVariableByName("SinSplit").AsVector().Set(new Vector2(SinSplitA, SinSplitB));
            //<<< _setup
            }

            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(_mesh.Vertices, 0) });
            context.D3DDevice.ImmediateContext.OutputMerger.DepthStencilState = new DepthStencilState(context.D3DDevice,
                                                                                                        new DepthStencilStateDescription()
                                                                                                            {
                                                                                                                IsDepthEnabled = false,
                                                                                                                IsStencilEnabled = false
                                                                                                            });
            context.D3DDevice.ImmediateContext.InputAssembler.InputLayout = context.InputLayout;
            context.D3DDevice.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            context.D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(inputMesh.Vertices, inputMesh.AttributesSize, 0));
            var technique = _effect.GetTechniqueByIndex(0);
            technique.GetPassByIndex(0).Apply(context.D3DDevice.ImmediateContext);
            context.D3DDevice.ImmediateContext.Draw(_mesh.NumTriangles*3, 0);
            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(null, 0) });

            Changed = false;
        }
        
        private SharpDX.ViewportF _usedViewport;

        private Mesh _mesh = new Mesh();
        private MeshCollector _meshCollector;
    }
}


