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

namespace Framefield.Core.IDb07a8577_3147_4c05_a584_0efbcb2a8b47
{
    public class Class_PickDepth : OperatorPart.Function
    {
        //>>> _outputids
        private enum OutputId
        {
            Depth = 0
        }
        //<<< _outputids
        //>>> _inputids
        private enum InputId
        {
            Image = 0,
            ScreenPositionX = 1,
            ScreenPositionY = 2
        }
        //<<< _inputids

        public override void Dispose()
        {
            foreach (var image in _depthImagesWithCpuAccess)
                image.Dispose();
            _depthImagesWithCpuAccess.Clear();
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {
            //>>> __params
            var ScreenPositionX = inputs[(int)InputId.ScreenPositionX].Eval(context).Value;
            var ScreenPositionY = inputs[(int)InputId.ScreenPositionY].Eval(context).Value;
            var ScreenPosition = new Vector2(ScreenPositionX, ScreenPositionY);
            //<<< __params

            if (Changed)
            {
                var depthImage = inputs[(int)InputId.Image].Eval(context).DepthImage;
                _lastEvaluationSucceeded = DoPicking(context, ScreenPosition, depthImage);
                if (_lastEvaluationSucceeded)
                    Changed = false;
            }

            context.Value = 0.0f;
            if (!_lastEvaluationSucceeded)
                return context;

            switch (outputIdx)
            {
                case (int)OutputId.Depth: context.Value = _lastEvaluatedDepth; break;
            }            
            return context;
        }

        bool DoPicking(OperatorPartContext context, Vector2 screenPosition, Texture2D depthImage)
        {
            if (screenPosition.X < -1 || screenPosition.X > 1 ||
                screenPosition.Y < -1 || screenPosition.Y > 1)
                return false;

            if (depthImage == null)
                return false;

            var currentDesc = depthImage.Description;
            if (_depthImagesWithCpuAccess.Count == 0 ||
                _depthImagesWithCpuAccess[0].Description.Format != currentDesc.Format ||
                _depthImagesWithCpuAccess[0].Description.Width != currentDesc.Width ||
                _depthImagesWithCpuAccess[0].Description.Height != currentDesc.Height ||
                _depthImagesWithCpuAccess[0].Description.MipLevels != currentDesc.MipLevels)
            {    
                var depthImageDesc = new Texture2DDescription
                                         {
                                             BindFlags = BindFlags.None,
                                             Format = currentDesc.Format,                                           
                                             Width = currentDesc.Width,
                                             Height = currentDesc.Height,
                                             MipLevels = currentDesc.MipLevels,
                                             SampleDescription = new SampleDescription(1, 0),
                                             Usage = ResourceUsage.Staging,
                                             OptionFlags = ResourceOptionFlags.None,
                                             CpuAccessFlags = CpuAccessFlags.Read,
                                             ArraySize = 1
                                         };

                Dispose();
                for (int i = 0; i < NUM_TEXTURE_ENTRIES; ++i)
                {
                    _depthImagesWithCpuAccess.Add(new Texture2D(D3DDevice.Device, depthImageDesc));
                }
                _currentIdx = 0;
                _currentUsage = 0;
            }

            var immediateContext = D3DDevice.Device.ImmediateContext;
            immediateContext.CopyResource(depthImage, _depthImagesWithCpuAccess[_currentIdx]);
            _currentIdx = ++_currentIdx % NUM_TEXTURE_ENTRIES;
            ++_currentUsage;

            if (_currentUsage >= NUM_TEXTURE_ENTRIES)
            {
                int x = (int)Utilities.Clamp(currentDesc.Width*(screenPosition.X + 1.0f)*0.5f, 0, currentDesc.Width - 1);
                int y = (int)Utilities.Clamp(currentDesc.Height*(screenPosition.Y - 1.0f)*-0.5f, 0, currentDesc.Height - 1);

                DataStream imageStream;
                immediateContext.UnmapSubresource(_depthImagesWithCpuAccess[_currentIdx], 0);
                DataBox dataBox = immediateContext.MapSubresource(_depthImagesWithCpuAccess[_currentIdx], 0, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None, out imageStream);
                using (imageStream)
                {
                    imageStream.Position = (long)(y - 1)*dataBox.RowPitch + (long)(x - 1)*4;
                    _lastEvaluatedDepth = imageStream.Read<float>();
                    imageStream.Position = (long)(y - 1)*dataBox.RowPitch + (long)(x + 1)*4;
                    _lastEvaluatedDepth += imageStream.Read<float>();
                    imageStream.Position = (long)y*dataBox.RowPitch + (long)x*4;
                    _lastEvaluatedDepth += imageStream.Read<float>();
                    imageStream.Position = (long)(y + 1)*dataBox.RowPitch + (long)(x - 1)*4;
                    _lastEvaluatedDepth += imageStream.Read<float>();
                    imageStream.Position = (long)(y + 1)*dataBox.RowPitch + (long)(x + 1)*4;
                    _lastEvaluatedDepth += imageStream.Read<float>();
                    _lastEvaluatedDepth /= 5;
                    immediateContext.UnmapSubresource(_depthImagesWithCpuAccess[_currentIdx], 0);
                }
            }
            return true;
        }

        const int NUM_TEXTURE_ENTRIES = 2;
        bool _lastEvaluationSucceeded;
        Texture2D _depthImageWithCPUAccess;
        float _lastEvaluatedDepth;
        List<Texture2D> _depthImagesWithCpuAccess = new List<Texture2D>();
        int _currentIdx;
        int _currentUsage;
    }
}

