using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;

namespace Framefield.Core.ID2f210b31_bf39_4c12_9721_f42585bdc3f1
{
    public class Class_Render_To_Img : OperatorPart.Function
    {
    // >>> _inputids
    private enum InputId
    {
        Scene = 0,
        SizeX = 1,
        SizeY = 2,
        ColorR = 3,
        ColorG = 4,
        ColorB = 5,
        ColorA = 6,
        SampleCount = 7,
        SampleQuality = 8,
        ClearBackground = 9
    }
    // <<< _inputids
        public override void Dispose() {
            ResourceManager.Dispose(_renderTargetResource);
            Utilities.DisposeObj(ref _renderTargetView);
            ResourceManager.Dispose(_renderDepthResource);
            Utilities.DisposeObj(ref _renderTargetDepthView);
        }

        private bool BuildRenderTarget(int width, int height, SharpDX.Direct3D11.Device device) {
            var renderTargetResourceChanged = ResourceManager.ValidateRenderTargetResource(ref _renderTargetResource, OperatorPart, device, width, height);
            if (renderTargetResourceChanged) {
                Utilities.DisposeObj(ref _renderTargetView);
                _renderTargetView = new RenderTargetView(device, _renderTargetResource.Texture);
            }

            var depthStencilResourceChanged = ResourceManager.ValidateDepthStencilResource(ref _renderDepthResource, OperatorPart, device, width, height);
            if (depthStencilResourceChanged) {
                Utilities.DisposeObj(ref _renderTargetDepthView);

                var depthViewDesc = new DepthStencilViewDescription();
                depthViewDesc.Format = Format.D32_Float;
                depthViewDesc.Dimension = DepthStencilViewDimension.Texture2D;

                _renderTargetDepthView = new DepthStencilView(device, _renderDepthResource.Texture, depthViewDesc);
            }

            return renderTargetResourceChanged || depthStencilResourceChanged;
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) {
            var Scene = inputs[0];

            //var rebuildTarget = inputs[1].Func.Changed ||
            //                    inputs[2].Func.Changed ||
            //                    inputs[3].Func.Changed ||
            //                    inputs[4].Func.Changed ||
            //                    inputs[5].Func.Changed ||
            //                    inputs[6].Func.Changed ||
            //                    inputs[7].Func.Changed ||
            //                    //inputs[8].Func.Changed ||
            //                    _renderTargetResource == null;

            int SizeX = (int) inputs[1].Eval(context).Value;
            int SizeY = (int) inputs[2].Eval(context).Value;
            float ColorR = inputs[3].Eval(context).Value;
            float ColorG = inputs[4].Eval(context).Value;
            float ColorB = inputs[5].Eval(context).Value;
            float ColorA = inputs[6].Eval(context).Value;
            bool ClearBackground = inputs[(int)InputId.ClearBackground].Eval(context).Value != 0;
            int SampleCount = (int) inputs[7].Eval(context).Value;
            //int SampleQuality = (int) inputs[8].Eval(context).Value;

            if (SampleCount != 1) {
                float sc = 0;
                if (context.Variables.TryGetValue("Samples", out sc))
                    SampleCount = (int)sc;
            }

            if (SizeX == 0 || SizeY == 0) {
                SizeX = (int) context.Viewport.Width;// * (SampleCount > 1 ? 2 : 1);
                SizeY = (int) context.Viewport.Height;// * (SampleCount > 1 ? 2 : 1);
                //if ((_renderTargetResource != null) && 
                //    (SizeX != _renderTargetResource.Texture.Description.Width || SizeY != _renderTargetResource.Texture.Description.Height)) {
                //    rebuildTarget = true;
                //}
            }

            if (BuildRenderTarget(SizeX, SizeY, context.D3DDevice))
                Changed = true;

            if (Changed) {
                var subContext = new OperatorPartContext(context);
                
                var D3DDevice = context.D3DDevice;

                float aspect = (float) SizeX / (float) SizeY;
                subContext.CameraProjection = Matrix.PerspectiveFovLH(3.1415f / 4.0f, aspect, 0.01f, 1000);
                subContext.WorldToCamera = Matrix.LookAtLH(new Vector3(0, 0, -10), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
                subContext.ObjectTWorld = Matrix.Identity;
                subContext.TextureMatrix = Matrix.Identity;
                subContext.Renderer = OperatorPartContext.DefaultRenderer;
                subContext.RenderTargetView = _renderTargetView;
                subContext.DepthStencilView = _renderTargetDepthView;
                subContext.Objects[OperatorPartContext.MATERIAL_ID] = new DefaultMaterial();

                subContext.DepthStencilState = OperatorPartContext.DefaultRenderer.DefaultDepthStencilState;
                subContext.BlendState = OperatorPartContext.DefaultRenderer.DefaultBlendState;
                subContext.BlendFactor = OperatorPartContext.DefaultRenderer.DefaultBlendFactor;
                subContext.Viewport = new Viewport(0, 0, SizeX, SizeY, 0.0f, 1.0f);
                
                subContext.InputLayout = OperatorPartContext.DefaultRenderer.SceneDefaultInputLayout;
                subContext.Effect = OperatorPartContext.DefaultRenderer.SceneDefaultEffect;
                //subContext.Texture0 = null;

                D3DDevice.ImmediateContext.ClearDepthStencilView(_renderTargetDepthView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
                if (ClearBackground) {
                    D3DDevice.ImmediateContext.ClearRenderTargetView(_renderTargetView, new SharpDX.Color4(ColorR, ColorG, ColorB, ColorA));
                }

                Scene.Eval(subContext);

                Changed = false;
            }

            context.Image = _renderTargetResource.Texture;
            context.DepthImage = _renderDepthResource.Texture;

            return context;
        }

        Resource _renderTargetResource;
        RenderTargetView _renderTargetView;
        Resource _renderDepthResource;
        DepthStencilView _renderTargetDepthView;
    }
}

