using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;

namespace Framefield.Core.ID8cc066ca_a65f_4b3a_bad7_cb143a567951
{
    public class Class_Bokeh : OperatorPart.Function
    {
        public Class_Bokeh() {
            try {
                using (var bytecode = ShaderBytecode.CompileFromFile("assets-common/fx/BokehBlur.fx", "fx_5_0", ShaderFlags.None, EffectFlags.None, null, null))
                    _effect = new Effect(D3DDevice.Device, bytecode);
            }
            catch (Exception exception) {
                Logger.Error(this,"Load Effect error: {0}", exception.Message);
            }
        }

        private bool BuildRenderTarget() {
            var resourceChanged = ResourceManager.ValidateRenderTargetResource(ref _renderTargetResource, OperatorPart, D3DDevice.Device,
                                                                               (int)_usedViewport.Width, (int)_usedViewport.Height);
            if (resourceChanged) {
                Utilities.DisposeObj(ref _renderTargetView);
                _renderTargetView = new RenderTargetView(D3DDevice.Device, _renderTargetResource.Texture);
            }
            return resourceChanged;
        }

        public override void Dispose() {
            ResourceManager.Dispose(_renderTargetResource);
            Utilities.DisposeObj(ref _renderTargetView);
            Utilities.DisposeObj(ref _effect);
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) {
            var Image = inputs[0].Eval(context).Image;
            var GlowFactor = inputs[1].Eval(context).Value;
            var Offset = inputs[2].Eval(context).Value;
            var Size = inputs[3].Eval(context).Value; 
            var Intensity = inputs[4].Eval(context).Value;
            var Threshold = inputs[5].Eval(context).Value;
            var Quality = (int)inputs[6].Eval(context).Value;

            if (context.Viewport.Width != _usedViewport.Width ||
                context.Viewport.Height != _usedViewport.Height) 
            {
                _usedViewport = context.Viewport;
                Changed = true;
            }

            if (BuildRenderTarget())
                Changed = true;

            if (Changed) {
                var D3DDevice = context.D3DDevice;
                var subContext = new OperatorPartContext(context);
                subContext.DepthStencilView = null;
                subContext.RenderTargetView = _renderTargetView;

                D3DDevice.ImmediateContext.ClearRenderTargetView(_renderTargetView, new SharpDX.Color4(0, 0, 0, 1));

                try {
                    subContext.Effect = _effect;
                    subContext.InputLayout = context.Renderer.ScreenQuadInputLayout;
                    var proj = Matrix.OrthoLH(1, 1, -100, 100);
                    subContext.CameraProjection = proj;
                    using (var textureView = new ShaderResourceView(D3DDevice, Image)) {
                        subContext.Texture0 = textureView;
                        subContext.Renderer.SetupEffect(subContext);

                        _effect.GetVariableByName("widthToHeight").AsScalar().Set((float)_usedViewport.Width/_usedViewport.Height);
                        _effect.GetVariableByName("glow").AsScalar().Set(GlowFactor);
                        _effect.GetVariableByName("offset").AsScalar().Set(Offset);
                        _effect.GetVariableByName("size").AsScalar().Set(Size);
                        _effect.GetVariableByName("intensity").AsScalar().Set(Intensity);
                        _effect.GetVariableByName("threshold").AsScalar().Set(Threshold);

                        //calculate hammersley point set in polar coordinates with uniform distribution for sampling 
                        //positions also known as a binary (t, m, s)-net where t = 0, m = Quality, s = 2
                        int numSamplePoints = 1 << Quality;
                        _effect.GetVariableByName("numSamplePoints").AsScalar().Set(numSamplePoints);

                        for (int i = 0; i < numSamplePoints; ++i) {
                            //sqrt is needed to create a uniform distribution wihtin the polar coordinate space
                            double r = Math.Sqrt((double)i/numSamplePoints);
                            double a = 2.0*Math.PI*(Reverse((ushort)i) >> (16-Quality))/numSamplePoints;
                            SharpDX.Vector2 pos = new SharpDX.Vector2((float)Math.Cos(a), (float)Math.Sin(a));
                            var varSamplePoint = _effect.GetVariableByName("samplePoints").GetElement(i).AsVector();
                            varSamplePoint.Set((float)r*pos);
                        }

                        subContext.Renderer.Render(subContext.Renderer._screenQuadMesh, subContext);
                    }
                }
                catch (Exception exception) {
                    Logger.Error(this,"Load Effect error: {0}", exception.Message);
                }

                Changed = false;
            }

            context.Image = _renderTargetResource.Texture;

            return context;
        }

        private static ushort Reverse(ushort v) {
            ushort r = v;
            int s = 15;
            for (v >>= 1; v != 0; v >>= 1) {
                r <<= 1;
                r |= (ushort)(v & 1);
                --s;
            }
            r <<= s;
            return r;
        }

        Resource _renderTargetResource = null;
        RenderTargetView _renderTargetView = null;
        Effect _effect;
        ViewportF _usedViewport = new ViewportF(0, 0, 512, 512);
    }
}

