//>>> _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 SharpDX.DXGI;
using Framefield.Core.OperatorPartTraits;
using Framefield.Core.Rendering;
using Buffer = SharpDX.Direct3D11.Buffer;

namespace Framefield.Core.ID1fa321b6_4ad2_4ba1_8670_3ec63588e42f
{
    public class Class_MeshBlend : FXSourceCodeFunction, IFXSceneSourceCode, IMeshSupplier
    {
        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            Scene = 1,
            BlendFactor = 2
        }
        //<<< _inputids

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

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

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

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            try
            {
                Logger.Debug(this, "bla");
                UpdateMesh(context, inputs);

                if (_blendedMesh.Vertices != null)
                {
                    // render stream output to screen or whatever target is set
                    context.Renderer.SetupEffect(context);
                    context.Renderer.Render(_blendedMesh, 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 && _blendedMesh.Vertices != null)
                return;

            //>>> _params
            var Code = inputs[(int)InputId.Code].Eval(context).Text;
            var Scene = inputs[(int)InputId.Scene];
            var BlendFactor = inputs[(int)InputId.BlendFactor].Eval(context).Value;
            //<<< _params

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

            if (_meshCollector.CollectedMeshes.Count != 1)
            {
                Logger.Error(this, "Found not exactly one martix mesh entry");
                return;
            }

            var firstEntry = _meshCollector.CollectedMeshes.First();
            if (firstEntry.Value.Count != 2)
            {
                Logger.Error(this, "Found not exactly 2 meshes.");
                return;
            }
            
            Logger.Debug(this, "bla01");
            var meshes = new Mesh[2];
            firstEntry.Value.CopyTo(meshes, 0);

            var firstMesh = meshes[0];
            var secondMesh = meshes[1];

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

            _effect.GetVariableByName("BlendFactor").AsScalar().Set(BlendFactor);
             var matrixEffectVariable = _effect.GetVariableByName("objectToWorldMatrix").AsMatrix();

            context.D3DDevice.ImmediateContext.OutputMerger.DepthStencilState = new DepthStencilState(context.D3DDevice,
                                                                                                        new DepthStencilStateDescription()
                                                                                                            {
                                                                                                                IsDepthEnabled = false,
                                                                                                                IsStencilEnabled = false
                                                                                                            });

            var technique = _effect.GetTechniqueByIndex(0);
            var pass = technique.GetPassByIndex(0);
            var inputElements = new[]
                                    {
                                        new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0),
                                        new InputElement("NORMAL", 0, Format.R32G32B32_Float, 16, 0),
                                        new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 28, 0),
                                        new InputElement("TEXCOORD", 0, Format.R32G32_Float, 44, 0),
                                        new InputElement("TANGENT", 0, Format.R32G32B32_Float, 52, 0),
                                        new InputElement("BINORMAL", 0, Format.R32G32B32_Float, 64, 0),
                                        new InputElement("POSITION", 1, Format.R32G32B32A32_Float, 0, 1),
                                        new InputElement("NORMAL", 1, Format.R32G32B32_Float, 16, 1),
                                        new InputElement("COLOR", 1, Format.R32G32B32A32_Float, 28, 1),
                                        new InputElement("TEXCOORD", 1, Format.R32G32_Float, 44, 1),
                                        new InputElement("TANGENT", 1, Format.R32G32B32_Float, 52, 1),
                                        new InputElement("BINORMAL", 1, Format.R32G32B32_Float, 64, 1)
                                    };
            var _inputLayout = new InputLayout(D3DDevice.Device, pass.Description.Signature, inputElements);

            context.D3DDevice.ImmediateContext.InputAssembler.InputLayout = _inputLayout;
            context.D3DDevice.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
            context.D3DDevice.ImmediateContext.PixelShader.Set(null);
            const int streamOutputOffset = 0;

            context.D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new [] { firstMesh.Vertices, secondMesh.Vertices}, new [] {76, 76}, new [] {0,0} );
            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(_blendedMesh.Vertices, streamOutputOffset) });
                                                                                          
//                    var technique = _effect.GetTechniqueByIndex(0);
            technique.GetPassByIndex(0).Apply(context.D3DDevice.ImmediateContext);
            context.D3DDevice.ImmediateContext.Draw(firstMesh.NumTriangles*3, 0);

            _inputLayout.Dispose();
            context.D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new Buffer[] {null, null}, new[]{0,0}, new[]{0,0});
            context.D3DDevice.ImmediateContext.StreamOutput.SetTargets(new [] { new StreamOutputBufferBinding(null, 0) });

            Changed = false;
        }

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


