#include "sierrachart.h"
#include <map>

SCDLLName("Rolling Value Area")

SCSFExport scsf_RollingValueArea(SCStudyInterfaceRef sc)
{
    SCSubgraphRef Subgraph_ValueAreaHigh = sc.Subgraph[0];
    SCSubgraphRef Subgraph_ValueAreaLow  = sc.Subgraph[1];
    SCSubgraphRef Subgraph_VPOC          = sc.Subgraph[2];

    SCInputRef Input_TimeWindowMinutes   = sc.Input[0];
    SCInputRef Input_ValueAreaPercentage = sc.Input[1];

    sc.MaintainVolumeAtPriceData = true;

    if (sc.SetDefaults)
    {
        sc.GraphName = "Rolling Value Area";
        sc.GraphRegion = 0;

        Subgraph_ValueAreaHigh.Name = "VAH";
        Subgraph_ValueAreaHigh.DrawStyle = DRAWSTYLE_STAIR_STEP;
        Subgraph_ValueAreaHigh.LineWidth = 4;
        Subgraph_ValueAreaHigh.PrimaryColor = RGB(0, 0, 255);

        Subgraph_ValueAreaLow.Name = "VAL";
        Subgraph_ValueAreaLow.DrawStyle = DRAWSTYLE_STAIR_STEP;
        Subgraph_ValueAreaLow.LineWidth = 4;
        Subgraph_ValueAreaLow.PrimaryColor = RGB(0, 0, 255);

        Subgraph_VPOC.Name = "VPOC";
        Subgraph_VPOC.DrawStyle = DRAWSTYLE_STAIR_STEP;
        Subgraph_VPOC.LineWidth = 2;
        Subgraph_VPOC.PrimaryColor = RGB(255, 255, 0);

        Input_TimeWindowMinutes.Name = "Rolling Window (Minutes)";
        Input_TimeWindowMinutes.SetInt(60);
        Input_TimeWindowMinutes.SetIntLimits(1, 1440);

        Input_ValueAreaPercentage.Name = "Value Area %";
        Input_ValueAreaPercentage.SetFloat(70.0f);
        Input_ValueAreaPercentage.SetFloatLimits(1.0f, 99.0f);

        sc.AutoLoop = 0;
        return;
    }

    for (int BarIndex = sc.UpdateStartIndex; BarIndex < sc.ArraySize; BarIndex++)
    {
        SCDateTime RollingStartTime = sc.BaseDateTimeIn[BarIndex] - (Input_TimeWindowMinutes.GetInt() / 1440.0);

        double TotalVolume = 0.0;
        std::map<double, double> AggregatedPriceVolume;

        for (int LookbackIndex = BarIndex; LookbackIndex >= 0; LookbackIndex--)
        {
            if (sc.BaseDateTimeIn[LookbackIndex] < RollingStartTime)
                break;

            double PointOfControl = 0.0;
            double ValueAreaHigh = 0.0;
            double ValueAreaLow = 0.0;

            sc.GetPointOfControlAndValueAreaPricesForBar(
                LookbackIndex, PointOfControl, ValueAreaHigh, ValueAreaLow, Input_ValueAreaPercentage.GetFloat()
            );

            AggregatedPriceVolume[ValueAreaHigh] += 1;
            AggregatedPriceVolume[ValueAreaLow] += 1;
            AggregatedPriceVolume[PointOfControl] += 1;
            TotalVolume += 3;
        }

        double VPOC = 0.0;
        double MaxVolume = 0.0;
        for (const auto& [price, volume] : AggregatedPriceVolume)
        {
            if (volume > MaxVolume)
            {
                MaxVolume = volume;
                VPOC = price;
            }
        }

        double ThresholdVolume = (Input_ValueAreaPercentage.GetFloat() / 100.0) * TotalVolume;
        double CumulativeVolume = 0.0;
        double VAH = 0.0, VAL = 0.0;

        for (const auto& [price, volume] : AggregatedPriceVolume)
        {
            CumulativeVolume += volume;
            if (CumulativeVolume >= ThresholdVolume)
            {
                VAH = price;
                break;
            }
        }

        CumulativeVolume = 0.0;
        for (auto it = AggregatedPriceVolume.rbegin(); it != AggregatedPriceVolume.rend(); ++it)
        {
            CumulativeVolume += it->second;
            if (CumulativeVolume >= ThresholdVolume)
            {
                VAL = it->first;
                break;
            }
        }

        Subgraph_ValueAreaHigh[BarIndex] = (float)VAH;
        Subgraph_ValueAreaLow[BarIndex]  = (float)VAL;
        Subgraph_VPOC[BarIndex]          = (float)VPOC;
    }
}
