//>>> _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;

namespace Framefield.Core.ID0ac6623c_f64d_41a9_b30d_d8f4fcb84943
{
    public class Class_DepthOfField : FXImageFunction
    {
        protected override bool NeedsDepth { get { return false; } }

        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            Image = 1,
            Camera = 2,
            Glow = 3,
            Offset = 4,
            Size = 5,
            NumberOfSamples = 6,
            Focus = 7,
            Range = 8
        }
        //<<< _inputids

        protected override bool BuildRenderTarget(SharpDX.DXGI.Format imageBufferFormat)
        {
            var pass1ResourceChanged = ResourceManager.ValidateRenderTargetResource(ref _pass1RenderTargetResource, OperatorPart, D3DDevice.Device,
                                                                                    (int)_usedViewport.Width, (int)_usedViewport.Height);
            if (pass1ResourceChanged)
            {
                Utilities.DisposeObj(ref _pass1RenderTargetView);
                _pass1RenderTargetView = new RenderTargetView(D3DDevice.Device, _pass1RenderTargetResource.Texture);
            }

            return base.BuildRenderTarget(imageBufferFormat) || pass1ResourceChanged;
        }

        public override void Dispose()
        {
            ResourceManager.Dispose(_pass1RenderTargetResource);
            Utilities.DisposeObj(ref _pass1RenderTargetView);
            base.Dispose();
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            return PrepareAndEvalOnChange(context, () =>
            {
                //>>> __params
                var Code = inputs[(int)InputId.Code].Eval(context).Text;
                var resultContextOfImageInput = inputs[(int)InputId.Image].Eval(context);
                var Image = resultContextOfImageInput.Image;
                var DepthImage = resultContextOfImageInput.DepthImage;
                var Camera = inputs[(int)InputId.Camera].Eval(context).Dynamic;
                var Glow = inputs[(int)InputId.Glow].Eval(context).Value;
                var Offset = inputs[(int)InputId.Offset].Eval(context).Value;
                var Size = inputs[(int)InputId.Size].Eval(context).Value;
                var NumberOfSamples = inputs[(int)InputId.NumberOfSamples].Eval(context).Value;
                var Focus = inputs[(int)InputId.Focus].Eval(context).Value;
                var Range = inputs[(int)InputId.Range].Eval(context).Value;
                //<<< __params

                if (Image == null || DepthImage == null || Camera == null)
                {
                    Logger.Error(this, "color or depth image or camera is not available");
                    return;
                }

                Framefield.Core.OperatorPartTraits.ICameraProvider camProvider = Camera.This as Framefield.Core.OperatorPartTraits.ICameraProvider;
                if (camProvider == null)
                {
                    Logger.Error(this, "camera not defined");
                    return;
                }

                var D3DDevice = context.D3DDevice;
                var proj = Matrix.OrthoLH(1, 1, -100, 100);
                var worldToCamera = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), new Vector3(0, 1, 0));

                var shaderDesc = new ShaderResourceViewDescription();
                shaderDesc.Format = Format.R32_Float;
                shaderDesc.Dimension = ShaderResourceViewDimension.Texture2D;
                shaderDesc.Texture2D.MipLevels = 1;
                using (var depthTexture = new ShaderResourceView(D3DDevice, DepthImage, shaderDesc))
                {
                    var viewToCamera = Matrix.Invert(camProvider.CalculateCameraToView(context));

                    _effect.GetVariableByName("viewToCamera").AsMatrix().SetMatrix(viewToCamera);
                    _effect.GetVariableByName("txDepth").AsShaderResource().SetResource(depthTexture); 
                    _effect.GetVariableByName("widthToHeight").AsScalar().Set((float)_usedViewport.Width/_usedViewport.Height);
                    _effect.GetVariableByName("Size").AsScalar().Set(Size);
                    _effect.GetVariableByName("Glow").AsScalar().Set(Glow);
                    _effect.GetVariableByName("Offset").AsScalar().Set(Offset);
                    _effect.GetVariableByName("NumberOfSamples").AsScalar().Set(NumberOfSamples);
                    _effect.GetVariableByName("Focus").AsScalar().Set(Focus);
                    _effect.GetVariableByName("Range").AsScalar().Set(Range);

                    //pass 1 (horizontal)
                    D3DDevice.ImmediateContext.ClearRenderTargetView(_pass1RenderTargetView, new SharpDX.Color4(0, 0, 0, 1));

                    var pass1SubContext = new OperatorPartContext(context);
                    pass1SubContext.DepthStencilView = null;
                    pass1SubContext.RenderTargetView = _pass1RenderTargetView;
                    pass1SubContext.Effect = _effect;
                    pass1SubContext.InputLayout = context.Renderer.ScreenQuadInputLayout;
                    pass1SubContext.CameraProjection = proj;
                    pass1SubContext.WorldToCamera = worldToCamera;
                    using (var textureView = new ShaderResourceView(D3DDevice, Image))
                    {
                        pass1SubContext.Texture0 = textureView;
                        pass1SubContext.Renderer.SetupEffect(pass1SubContext);

                        _effect.GetVariableByName("direction").AsVector().Set(new Vector2(1, 0));

                        pass1SubContext.Renderer.Render(pass1SubContext.Renderer._screenQuadMesh, pass1SubContext);
                    }

                    //pass 2 (vertical)
                    D3DDevice.ImmediateContext.ClearRenderTargetView(_renderTargetView, new SharpDX.Color4(0, 0, 0, 1));

                    var pass2SubContext = new OperatorPartContext(context);
                    pass2SubContext.DepthStencilView = null;
                    pass2SubContext.RenderTargetView = _renderTargetView;
                    pass2SubContext.Effect = _effect;
                    pass2SubContext.InputLayout = context.Renderer.ScreenQuadInputLayout;
                    pass2SubContext.CameraProjection = proj;
                    pass2SubContext.WorldToCamera = worldToCamera;
                    using (var textureView = new ShaderResourceView(D3DDevice, _pass1RenderTargetResource.Texture))
                    {
                        pass2SubContext.Texture0 = textureView;
                        pass2SubContext.Renderer.SetupEffect(pass2SubContext);

                        _effect.GetVariableByName("direction").AsVector().Set(new Vector2(0, 1));

                        pass2SubContext.Renderer.Render(pass2SubContext.Renderer._screenQuadMesh, pass2SubContext);
                    }
                }
            });
        }

        Resource _pass1RenderTargetResource = null;
        RenderTargetView _pass1RenderTargetView = null;
    }
}
