//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using SharpDX.DXGI;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D; 
using Framefield.Core.Rendering;
using System.Dynamic;
using Framefield.Core;

namespace Framefield.Core.IDccf5e5de_d7eb_457e_9501_9e06626d90eb
{
    public class Class_GodRaysFromLight : FXImageFunction
    {
        //>>> _inputids
        private enum InputId
        {
            Code = 0,
            Image = 1,
            Camera = 2,
            Strength = 3,
            Rays = 4,
            NumberOfSamples = 5,
            Size = 6,
            PointLight = 7,
            ColorR = 8,
            ColorG = 9,
            ColorB = 10,
            ColorA = 11,
            ShiftDepth = 12,
            Brigthness = 13,
            Contrast = 14,
            NumberOfBlurSamples = 15,
            Offset = 16,
            BlurSize = 17,
            Enabled = 18
        }
        //<<< _inputids

        protected override bool NeedsDepth { get { return false; } }

        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 imageInput = inputs[(int)InputId.Image].Eval(context);
                var Camera = inputs[(int)InputId.Camera].Eval(context).Dynamic;
                var Strength = inputs[(int)InputId.Strength].Eval(context).Value;
                var Rays = inputs[(int)InputId.Rays].Eval(context).Value;
                var NumberOfSamples = inputs[(int)InputId.NumberOfSamples].Eval(context).Value;
                var Size = inputs[(int)InputId.Size].Eval(context).Value;
                var PointLight = inputs[(int)InputId.PointLight].Eval(context).Dynamic;
                var ColorR = inputs[(int)InputId.ColorR].Eval(context).Value;
                var ColorG = inputs[(int)InputId.ColorG].Eval(context).Value;
                var ColorB = inputs[(int)InputId.ColorB].Eval(context).Value;
                var ColorA = inputs[(int)InputId.ColorA].Eval(context).Value;
                var Color = new Color4(ColorR, ColorG, ColorB, ColorA);
                var ShiftDepth = inputs[(int)InputId.ShiftDepth].Eval(context).Value;
                var Brigthness = inputs[(int)InputId.Brigthness].Eval(context).Value;
                var Contrast = inputs[(int)InputId.Contrast].Eval(context).Value;
                var NumberOfBlurSamples = inputs[(int)InputId.NumberOfBlurSamples].Eval(context).Value;
                var Offset = inputs[(int)InputId.Offset].Eval(context).Value;
                var BlurSize = inputs[(int)InputId.BlurSize].Eval(context).Value;
                //<<< __params

                var image = imageInput.Image;
                var depthImage = imageInput.DepthImage;
                if (image == null || depthImage == null || Camera == null || PointLight == null)
                {
                    Logger.Error(this, "conditions not fulfilled");
                    return;
                }
                Framefield.Core.OperatorPartTraits.ICameraProvider camProvider = Camera.This as Framefield.Core.OperatorPartTraits.ICameraProvider;
                if (camProvider == null)
                {
                    Logger.Error(this, "camera not defined");
                    return;
                }
                Framefield.Core.IPointLight pointLight = PointLight.This as Framefield.Core.IPointLight;
                if (pointLight == null)
                {
                    Logger.Error(this, "pointlight not defined");
                    return;
                }
    
                if (_renderer2 == null)
                {
                     _renderer2 = new BaseRenderer();
                }

                var D3DDevice = context.D3DDevice;

                var worldToCamera = camProvider.CalculateWorldToCamera(context);
                var cameraToView = camProvider.CalculateCameraToView(context);
                var viewToCamera = Matrix.Invert(cameraToView);
                var worldToView = worldToCamera*cameraToView;

                var shaderDesc = new ShaderResourceViewDescription();
                shaderDesc.Format = Format.R32_Float;
                shaderDesc.Dimension = ShaderResourceViewDimension.Texture2D;
                shaderDesc.Texture2D.MipLevels = 1;

                var pointLigthPos4InWorld = new Vector4(pointLight.Position.X, pointLight.Position.Y, pointLight.Position.Z, 1);
                var pointLightPos4InCamera = Vector4.Transform(pointLigthPos4InWorld, worldToCamera);
                var pointLightPos3InCamera = new Vector3(pointLightPos4InCamera.X, pointLightPos4InCamera.Y, pointLightPos4InCamera.Z);
                pointLightPos3InCamera /= pointLightPos4InCamera.W;

                var pointLightPos4InView = Vector4.Transform(pointLigthPos4InWorld, worldToView);
                var pointLightPos3InView = new Vector3(pointLightPos4InView.X, pointLightPos4InView.Y, pointLightPos4InView.Z);
                pointLightPos3InView /= pointLightPos4InView.W;

                //pass 1: original godrays
                using (var depthTexture = new ShaderResourceView(context.D3DDevice, depthImage, shaderDesc))
                using (var ImageView = new ShaderResourceView(context.D3DDevice, image))
                {
                    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.Renderer = _renderer;
                    pass1SubContext.InputLayout = context.Renderer.ScreenQuadInputLayout;
                    pass1SubContext.CameraProjection = Matrix.OrthoLH(1, 1, -100, 100);
                    pass1SubContext.WorldToCamera = Matrix.Identity;
                    pass1SubContext.ObjectTWorld = Matrix.Identity;
                    pass1SubContext.TextureMatrix = Matrix.Identity;

                    pass1SubContext.Renderer.SetupEffect(pass1SubContext);

                    _effect.GetVariableByName("RenderTargetSize").AsVector().Set(new Vector2(_usedViewport.Width, _usedViewport.Height));
                    _effect.GetVariableByName("Image").AsShaderResource().SetResource(ImageView);
                    _effect.GetVariableByName("Strength").AsScalar().Set(Strength);
                    _effect.GetVariableByName("Rays").AsScalar().Set(Rays);
                    _effect.GetVariableByName("ShiftDepth").AsScalar().Set(ShiftDepth);
                    _effect.GetVariableByName("Size").AsScalar().Set(Size);
                    _effect.GetVariableByName("NumberOfSamples").AsScalar().Set(NumberOfSamples);
                    _effect.GetVariableByName("Color").AsVector().Set(Color);
                    _effect.GetVariableByName("CenterInCamera").AsVector().Set(pointLightPos3InCamera);
                    _effect.GetVariableByName("CenterInView").AsVector().Set(pointLightPos3InView);
                    _effect.GetVariableByName("viewToCamera").AsMatrix().SetMatrix(viewToCamera);
                    _effect.GetVariableByName("txDepth").AsShaderResource().SetResource(depthTexture);

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

                //pass 2 directional blur
                using (var textureView = new ShaderResourceView(D3DDevice, _pass1RenderTargetResource.Texture))
                {
                    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.Renderer = _renderer2;
                    pass2SubContext.InputLayout = context.Renderer.ScreenQuadInputLayout;
                    pass2SubContext.CameraProjection = Matrix.OrthoLH(1, 1, -100, 100);
                    pass2SubContext.WorldToCamera = Matrix.Identity;
                    pass2SubContext.ObjectTWorld = Matrix.Identity;
                    pass2SubContext.TextureMatrix = Matrix.Identity;
                    pass2SubContext.Texture0 = textureView;
                    pass2SubContext.Renderer.SetupEffect(pass2SubContext);

                    _effect.GetVariableByName("Brightness").AsScalar().Set(Brigthness);
                    _effect.GetVariableByName("Contrast").AsScalar().Set(Contrast);
                    _effect.GetVariableByName("NumberOfBlurSamples").AsScalar().Set(NumberOfBlurSamples);
                    _effect.GetVariableByName("Offset").AsScalar().Set(Offset);
                    _effect.GetVariableByName("BlurSize").AsScalar().Set(BlurSize);

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

            });
        }

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

