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

namespace Framefield.Core.IDcd8cd1ea_1d0e_46ef_a6e0_ce180fc15a2a
{
    public class Class_TransformMesh : FXSourceCodeFunction, IFXSceneSourceCode
    {
        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            Geometry = 1,
            TranslateX = 2,
            TranslateY = 3,
            TranslateZ = 4,
            RotateX = 5,
            RotateY = 6,
            RotateZ = 7,
            ScaleX = 8,
            ScaleY = 9,
            ScaleZ = 10,
            PivotX = 11,
            PivotY = 12,
            PivotZ = 13
        }
        //<<< _inputids

        public Class_TransformMesh()
        {
        }

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

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            try
            {
                UpdateMesh(context, inputs);
                context.Mesh = _bakedMesh;
            }
            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 && _bakedMesh.Vertices != null)
                return;

            //>>> _params
            var Code = inputs[(int)InputId.Code].Eval(context).Text;
            var Geometry = inputs[(int)InputId.Geometry].Eval(context).Mesh;
            var TranslateX = inputs[(int)InputId.TranslateX].Eval(context).Value;
            var TranslateY = inputs[(int)InputId.TranslateY].Eval(context).Value;
            var TranslateZ = inputs[(int)InputId.TranslateZ].Eval(context).Value;
            var Translate = new Vector3(TranslateX, TranslateY, TranslateZ);
            var RotateX = inputs[(int)InputId.RotateX].Eval(context).Value;
            var RotateY = inputs[(int)InputId.RotateY].Eval(context).Value;
            var RotateZ = inputs[(int)InputId.RotateZ].Eval(context).Value;
            var Rotate = new Vector3(RotateX, RotateY, RotateZ);
            var ScaleX = inputs[(int)InputId.ScaleX].Eval(context).Value;
            var ScaleY = inputs[(int)InputId.ScaleY].Eval(context).Value;
            var ScaleZ = inputs[(int)InputId.ScaleZ].Eval(context).Value;
            var Scale = new Vector3(ScaleX, ScaleY, ScaleZ);
            var PivotX = inputs[(int)InputId.PivotX].Eval(context).Value;
            var PivotY = inputs[(int)InputId.PivotY].Eval(context).Value;
            var PivotZ = inputs[(int)InputId.PivotZ].Eval(context).Value;
            var Pivot = new Vector3(PivotX, PivotY, PivotZ);
            //<<< _params

            var inputMesh = Geometry;
            if (inputMesh == null)
            {
                Utilities.DisposeObj(ref _bakedMesh.Vertices);
                return;
            }

            // Compute size of required vertex buffer and create it
            _bakedMesh.NumTriangles = inputMesh.NumTriangles;

            _bakedMesh.AttributesSize = inputMesh.AttributesSize;
            _bakedMesh.InputElements = inputMesh.InputElements;
            if (_bakedMesh.Vertices == null || _bakedMesh.Vertices.Description.SizeInBytes != inputMesh.Vertices.Description.SizeInBytes)
            {
                Utilities.DisposeObj(ref _bakedMesh.Vertices);
                _bakedMesh.Vertices = new Buffer(D3DDevice.Device, new BufferDescription()
                                                                  {
                                                                      BindFlags = BindFlags.StreamOutput | BindFlags.VertexBuffer,
                                                                      CpuAccessFlags = CpuAccessFlags.None,
                                                                      OptionFlags = ResourceOptionFlags.None,
                                                                      SizeInBytes = _bakedMesh.NumTriangles*_bakedMesh.AttributesSize*3,
                                                                      Usage = ResourceUsage.Default
                                                                  });
            }

            // Initialize effect
            var rotation = Quaternion.RotationYawPitchRoll(MathUtil.DegreesToRadians(RotateY),
                                                           MathUtil.DegreesToRadians(RotateX),
                                                           MathUtil.DegreesToRadians(RotateZ));

            var transform = Matrix.Transformation(Pivot, Quaternion.Zero, Scale, Pivot, rotation, Translate);

            var matrixEffectVariable = _effect.GetVariableByName("objectToWorldMatrix").AsMatrix();
            matrixEffectVariable.SetMatrix(transform);

            context.D3DDevice.ImmediateContext.InputAssembler.InputLayout = context.InputLayout;
            context.D3DDevice.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

            // Configure Pipeline for stream output
            int streamOutputOffset = 0;
            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(_bakedMesh.Vertices, streamOutputOffset) });
            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);
            
            // Draw mesh into vertex buffer
            context.D3DDevice.ImmediateContext.Draw(inputMesh.NumTriangles*3, 0);
            streamOutputOffset += inputMesh.AttributesSize*inputMesh.NumTriangles*3;

            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(null, 0) });

            Changed = false;
        }

        private Mesh _bakedMesh = new Mesh();
    }
}


