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

namespace Framefield.Core.ID1c3ba139_af90_47bd_ba3e_a66e9897d043
{
    public class Class_MemoryGraph : OperatorPart.Function,
                                     Framefield.Core.OperatorPartTraits.ITimeAccessor
    {
        //>>> _inputids
        private enum InputId
        {
            TimeStart = 0,
            TimeEnd = 1,
            Resolution = 2
        }
        //<<< _inputids

        public override void Dispose()
        {
            PlaneMesh.Dispose();
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx)
        {
            //>>> _params
            var TimeStart = inputs[(int)InputId.TimeStart].Eval(context).Value;
            var TimeEnd = inputs[(int)InputId.TimeEnd].Eval(context).Value;
            var Time = new Vector2(TimeStart, TimeEnd);
            var Resolution = inputs[(int)InputId.Resolution].Eval(context).Value;
            //<<< _params

            var logData = TimeLogger.LogData;
            if (!logData.Any())
                return context;

            try
            {
                if (_lastUpdateTime != logData.Last().Value.StartTime)
                    Changed = true;

                if (Changed)
                {
                    var normal = new Vector3(0.0f, 0.0f, -1.0f);
                    var color = new Vector4(0.9f, 0.4f, 0.6f, 0.6f);
                    var tangent = new Vector3(1.0f, 0.0f, 0.0f);
                    var binormal = new Vector3(0.0f, -1.0f, 0.0f);

                    var inputElements = new InputElement[]
                                            {
                                                new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 0, 0),
                                                new InputElement("NORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 16, 0),
                                                new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 28, 0),
                                                new InputElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, 44, 0),
                                                new InputElement("TANGENT", 0, SharpDX.DXGI.Format.R32G32B32_Float, 52, 0),
                                                new InputElement("BINORMAL", 0, SharpDX.DXGI.Format.R32G32B32_Float, 64, 0)
                                            };

                    // Collect sample points for timeframe                    
                    var pointList = new List<float[]>();

                    double now = logData.Last().Value.StartTime;
                    _lastUpdateTime = logData.First().Value.StartTime;

                    double lastTime = now;

                    float sumx = 0;
                    float sumy = 0;
                    int countClusterPoint = 0;
                    float resolutionStep = 1/Resolution;
                    float nextStepThreshold = resolutionStep;

                    foreach (var sample in logData)
                    {
                        if (sample.Value.StartTime > now - TimeEnd)
                            break;
                        if (sample.Value.StartTime < now - TimeStart)
                            continue;

                        var t = sample.Value.StartTime;
                        if (t == lastTime)
                            continue;

                        float x = (float)((t - (now - TimeStart)) / (TimeStart - TimeEnd));
                        float y = (float)(sample.Value.PrivateMemory/(1 << 20));
                        lastTime = t;

                        sumx += x;
                        sumy += y;
                        ++countClusterPoint;

                        if (x > nextStepThreshold)
                        {
                            pointList.Add(new float[] {sumx/countClusterPoint, sumy/countClusterPoint});
                            nextStepThreshold = ((float)Math.Floor(x/resolutionStep) + 1)*resolutionStep;
                            sumx = 0;
                            sumy = 0;
                            countClusterPoint = 0;
                        }
                    }
                    if (countClusterPoint > 0)
                    {
                        pointList.Add(new float[] {sumx/countClusterPoint, sumy/countClusterPoint});
                    }

                    // Generate mesh
                    var attributesSize = 76;
                    int numTriangles = (pointList.Count - 2)*2;
                    int streamSize = Math.Max(numTriangles * 3 * attributesSize, 0);
                    using (var vertexStream = new DataStream(streamSize, true, true))
                    {
                        float bottom = 0;
                        for (int i = 0; i < pointList.Count - 2; ++i)
                        {
                            float left = pointList[i][0];
                            float topLeft = (pointList[i][1] + pointList[i + 1][1])/2.0f; // smooth
                            float topRight = (pointList[i][1] + pointList[i + 1][1])/2.0f; // smooth
                            float right = pointList[i + 1][0];

                            // tri 1 vert 1
                            vertexStream.Write(new Vector4(right, topRight, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);

                            // tri 1 vert 2
                            vertexStream.Write(new Vector4(right, bottom, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);

                            // tri 1 vert 3
                            vertexStream.Write(new Vector4(left, bottom, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);

                            // tri 2 vert 1
                            vertexStream.Write(new Vector4(left, bottom, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);

                            // tri 2 vert 2
                            vertexStream.Write(new Vector4(left, topLeft, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);

                            // tri 2 vert 3
                            vertexStream.Write(new Vector4(right, topRight, 0, 1));
                            vertexStream.Write(normal);
                            vertexStream.Write(color);
                            vertexStream.Write(new Vector2(0, 1.0f));
                            vertexStream.Write(tangent);
                            vertexStream.Write(binormal);
                        }

                        vertexStream.Position = 0;
                        var vertices = new Buffer(context.D3DDevice,
                                                  vertexStream,
                                                  new BufferDescription()
                                                  {
                                                      BindFlags = BindFlags.VertexBuffer,
                                                      CpuAccessFlags = CpuAccessFlags.None,
                                                      OptionFlags = ResourceOptionFlags.None,
                                                      SizeInBytes = streamSize,
                                                      Usage = ResourceUsage.Default
                                                  });

                        PlaneMesh.Dispose();
                        PlaneMesh = new Mesh() {InputElements = inputElements, Vertices = vertices, NumTriangles = numTriangles, AttributesSize = attributesSize};
                    }
                    Changed = false;
                }
                context.Renderer.SetupEffect(context);
                context.Renderer.Render(PlaneMesh, context);
            }
            catch (Exception e)
            {
                Logger.Error(this,"error {0}", e);
            }

            return context;
        }

        private Mesh PlaneMesh = new Mesh();
        private double _lastUpdateTime = -1;
    }
}