//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using Framefield.Core.Profiling;

namespace Framefield.Core.IDf4b28c70_5dfa_426a_ade9_624ab3ac9cd3
{
    public class Class_Measure : OperatorPart.Function
    {
        //>>> _inputids
        private enum InputId
        {
            Input = 0,
            ColorR = 1,
            ColorG = 2,
            ColorB = 3,
            ColorA = 4
        }
        //<<< _inputids
        
        //>>> _outputids
        private enum OutputId
        {
            Input = 0,
            Time = 1,
            RenderedPrimitives = 2,
            PrimitivesSentToRasterizer = 3
        }
        //<<< _outputids

        public Class_Measure()
        {
            _measureElements = new MeasureElement[_maxQueryFrames];
            for (int i = 0; i < _maxQueryFrames; ++i)
            {
                _measureElements[i] = new MeasureElement
                                      {
                                          FrameID = 0,
                                          QueryTimeStampDisjoint = new GPUQuery(D3DDevice.Device, new QueryDescription() { Type = QueryType.TimestampDisjoint }),
                                          QueryTimeStampFrameBegin = new GPUQuery(D3DDevice.Device, new QueryDescription() { Type = QueryType.Timestamp }),
                                          QueryTimeStampStart = new GPUQuery(D3DDevice.Device, new QueryDescription() { Type = QueryType.Timestamp }),
                                          QueryTimeStampEnd = new GPUQuery(D3DDevice.Device, new QueryDescription() { Type = QueryType.Timestamp }),
                                          QueryPipelineStats = new GPUQuery(D3DDevice.Device, new QueryDescription() { Type = QueryType.PipelineStatistics })
                                      };
            }
            _currentQueryFrame = 0;
            _allQueriesValid = false;

            D3DDevice.BeginFrameEvent += HandleBeginFrame;
            D3DDevice.EndFrameEvent += HandleEndFrame;
        }

        public override void Dispose()
        {
            D3DDevice.BeginFrameEvent -= HandleBeginFrame;
            D3DDevice.EndFrameEvent -= HandleEndFrame;

            var context = D3DDevice.Device.ImmediateContext;
            for (int i = 0; i < _maxQueryFrames; ++i)
            {
                _measureElements[i].Dispose();
            }
        }

        void HandleBeginFrame()
        {
            if (!TimeLogger.Enabled)
                return;

            var measureElement = _measureElements[_currentQueryFrame];
            measureElement.FrameID = TimeLogger.FrameID;
            measureElement.QueryTimeStampDisjoint.Begin(D3DDevice.Device.ImmediateContext);
            measureElement.QueryTimeStampFrameBegin.End(D3DDevice.Device.ImmediateContext);
        }

        void HandleEndFrame()
        {
            if (!TimeLogger.Enabled)
                return;

            var context = D3DDevice.Device.ImmediateContext;
            _measureElements[_currentQueryFrame].QueryTimeStampDisjoint.End(context);

            int oldestQueryFrame = (_currentQueryFrame + 1) % _maxQueryFrames;
            ++_currentQueryFrame;
            if (_currentQueryFrame == _maxQueryFrames)
                _allQueriesValid = true;

            _currentQueryFrame %= _maxQueryFrames;

            if (_allQueriesValid)
            {
                var measureElementToFetch = _measureElements[oldestQueryFrame];
                QueryDataTimestampDisjoint disjointData;
                long timeStampframeBegin;
                long timeStampStart;
                long timeStampEnd;
                QueryDataPipelineStatistics pipelineStatsData;
                bool dataFetched = true;
                dataFetched &= measureElementToFetch.QueryTimeStampDisjoint.GetData(context, AsynchronousFlags.None, out disjointData);
                dataFetched &= measureElementToFetch.QueryTimeStampFrameBegin.GetData(context, AsynchronousFlags.None, out timeStampframeBegin);
                dataFetched &= measureElementToFetch.QueryTimeStampStart.GetData(context, AsynchronousFlags.None, out timeStampStart);
                dataFetched &= measureElementToFetch.QueryTimeStampEnd.GetData(context, AsynchronousFlags.None, out timeStampEnd);
                dataFetched &= measureElementToFetch.QueryPipelineStats.GetData(context, AsynchronousFlags.None, out pipelineStatsData);

                if (dataFetched && !disjointData.Disjoint)
                {
                    var entry = new DataEntry()
                                    {
                                        ID = GetHashCode().ToString(),
                                        Name = OperatorPart.Parent.Name,
                                        Color = System.Drawing.Color.FromArgb((int) (_color.Alpha*255.0), (int) (_color.Red*255.0), (int) (_color.Green*255.0), (int) (_color.Blue*255.0)),
                                        Duration = (double)(timeStampEnd - timeStampStart)/disjointData.Frequency,
                                        FrameTimeOffset = (double)(timeStampStart - timeStampframeBegin)/disjointData.Frequency
                                    };
                    TimeLogger.Add(measureElementToFetch.FrameID, entry);

                    _lastTime = (float)entry.Duration;
                    _lastRenderedPrimitives = pipelineStatsData.CPrimitiveCount;
                    _lastPrimitivesSentToRasterizer = pipelineStatsData.CInvocationCount;
                }
            }
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            //>>> _params
            var Input = inputs[(int)InputId.Input];
            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);
            //<<< _params

            _color = Color;

            if (TimeLogger.Enabled)
            {
                switch (outputIdx)
                {
                    case (int)OutputId.Input:
                        var measureElement = _measureElements[_currentQueryFrame];
                        measureElement.QueryTimeStampStart.End(context.D3DDevice.ImmediateContext);
                        measureElement.QueryPipelineStats.Begin(context.D3DDevice.ImmediateContext);
                        Input.Eval(context);
                        measureElement.QueryTimeStampEnd.End(context.D3DDevice.ImmediateContext);
                        measureElement.QueryPipelineStats.End(context.D3DDevice.ImmediateContext);

                        OperatorPart.EmitChangedEventForOutput((int)OutputId.Time);
                        OperatorPart.EmitChangedEventForOutput((int)OutputId.RenderedPrimitives);
                        OperatorPart.EmitChangedEventForOutput((int)OutputId.PrimitivesSentToRasterizer);
                        break;
                    case (int)OutputId.Time: context.Value = _lastTime; break;
                    case (int)OutputId.RenderedPrimitives: context.Value = _lastRenderedPrimitives; break;
                    case (int)OutputId.PrimitivesSentToRasterizer: context.Value = _lastPrimitivesSentToRasterizer; break;
                }
            }
            else
            {
                switch (outputIdx)
                {
                    case (int)OutputId.Input: Input.Eval(context); break;
                    case (int)OutputId.Time: context.Value = 0.0f; break;
                    case (int)OutputId.RenderedPrimitives: context.Value = 0.0f; break;
                    case (int)OutputId.PrimitivesSentToRasterizer: context.Value = 0.0f; break;
                }
            }
            return context;
        }

        public class MeasureElement : IDisposable
        {
            public Int64 FrameID { get; set; }
            public GPUQuery QueryTimeStampFrameBegin { get; set; }
            public GPUQuery QueryTimeStampDisjoint { get; set; }
            public GPUQuery QueryTimeStampStart { get; set; }
            public GPUQuery QueryTimeStampEnd { get; set; }
            public GPUQuery QueryPipelineStats { get; set; }

            public void Dispose()
            {
                FrameID = 0;
                QueryTimeStampFrameBegin.Dispose();
                QueryTimeStampDisjoint.Dispose();
                QueryTimeStampStart.Dispose();
                QueryTimeStampEnd.Dispose();
                QueryPipelineStats.Dispose();
            }
        }

        Color4 _color;
        int _currentQueryFrame;
        int _maxQueryFrames = 5;
        bool _allQueriesValid = false;
        MeasureElement[] _measureElements;

        float _lastTime;
        float _lastRenderedPrimitives;
        float _lastPrimitivesSentToRasterizer;
    }
}
