#include "sierrachart.h"

SCDLLName("Simplified Leg to Leg Volume Profile")

struct ProfileData 
{
    std::set<float> prices;
    std::map<float, int> totalVolumeAtPrice; // For total volume
    std::map<float, int> deltaVolumeAtPrice;  // For delta volume
    float anchorPrice;
    float profileHigh;
    float profileLow;
    bool isUptrend;
    int anchorBarIndex;
};

// Drawing function declaration
void DrawVolumeProfile(HWND WindowHandle, HDC DeviceContext, SCStudyInterfaceRef sc);

SCSFExport scsf_SimplifiedLegToLegVolumeProfile(SCStudyInterfaceRef sc)
{
    if (sc.SetDefaults)
    {
        sc.GraphName = "Simplified Leg to Leg Volume Profile";
        sc.GraphRegion = 0;
        sc.AutoLoop = 1;
        sc.MaintainVolumeAtPriceData = 1;
        
        sc.Input[0].Name = "Rotation Threshold (Points)";
        sc.Input[0].SetFloat(20.0f);
        sc.Input[0].SetFloatLimits(0.1f, 1000.0f);

        sc.Input[1].Name = "Volume Scale";
        sc.Input[1].SetFloat(1.0f);
        sc.Input[1].SetFloatLimits(0.001f, 10.0f);

        sc.Input[2].Name = "Display Delta";
        sc.Input[2].SetYesNo(0); // Default to total volume, 0 = No, 1 = Yes
    }

    sc.p_GDIFunction = DrawVolumeProfile;
}

void DrawVolumeProfile(HWND WindowHandle, HDC DeviceContext, SCStudyInterfaceRef sc)
{
    static ProfileData currentProfile, previousProfile;
    const float ROTATION_THRESHOLD = sc.Input[0].GetFloat();
    const float VOLUME_SCALE = sc.Input[1].GetFloat();
    const bool DISPLAY_DELTA = sc.Input[2].GetYesNo(); // Get the setting for delta display

    float currentPrice = sc.Close[sc.Index];
    float currentHigh = sc.High[sc.Index];
    float currentLow = sc.Low[sc.Index];

    if (currentProfile.prices.empty())  // New profile on chart initialization or after reset
    {
        currentProfile.isUptrend = currentPrice < currentHigh; // Guess trend based on bar's range
        currentProfile.anchorPrice = currentProfile.isUptrend ? currentLow : currentHigh;
        currentProfile.profileHigh = currentHigh;
        currentProfile.profileLow = currentLow;
        currentProfile.anchorBarIndex = sc.Index; // Set anchor bar index on initialization
    }
    else
    {
        // Update profile high and low
        currentProfile.profileHigh = max(currentProfile.profileHigh, currentHigh);
        currentProfile.profileLow = min(currentProfile.profileLow, currentLow);

        // Check for rotation
        if (currentProfile.isUptrend && currentLow < currentProfile.profileHigh - ROTATION_THRESHOLD)
        {
            // New downtrend profile
            previousProfile = currentProfile;
            currentProfile.isUptrend = false;
            currentProfile.anchorPrice = previousProfile.profileHigh; // Anchor at previous high
            // Find the bar index where the high occurred
            for (int i = sc.Index; i >= 0; --i)
            {
                if (sc.High[i] == previousProfile.profileHigh)
                {
                    currentProfile.anchorBarIndex = i;
                    break;
                }
            }
            currentProfile.prices.clear();
            currentProfile.totalVolumeAtPrice.clear();
            currentProfile.deltaVolumeAtPrice.clear();
            currentProfile.profileHigh = currentHigh;
            currentProfile.profileLow = currentLow;
        }
        else if (!currentProfile.isUptrend && currentHigh > currentProfile.profileLow + ROTATION_THRESHOLD)
        {
            // New uptrend profile
            previousProfile = currentProfile;
            currentProfile.isUptrend = true;
            currentProfile.anchorPrice = previousProfile.profileLow; // Anchor at previous low
            // Find the bar index where the low occurred
            for (int i = sc.Index; i >= 0; --i)
            {
                if (sc.Low[i] == previousProfile.profileLow)
                {
                    currentProfile.anchorBarIndex = i;
                    break;
                }
            }
            currentProfile.prices.clear();
            currentProfile.totalVolumeAtPrice.clear();
            currentProfile.deltaVolumeAtPrice.clear();
            currentProfile.profileHigh = currentHigh;
            currentProfile.profileLow = currentLow;
        }
    }

    // Collect volume data for the current profile from the anchor point
    for (int i = currentProfile.anchorBarIndex; i <= sc.Index; ++i) // Start from the anchor bar
    {
        float barHigh = sc.High[i];
        float barLow = sc.Low[i];

        float startPrice = currentProfile.isUptrend ? max(barLow, currentProfile.anchorPrice) : barLow;
        float endPrice = currentProfile.isUptrend ? barHigh : min(barHigh, currentProfile.anchorPrice);

        for (float price = startPrice; price <= endPrice; price += sc.TickSize)
        {
            currentProfile.prices.insert(price);

            int VAPSizeAtBarIndex = sc.VolumeAtPriceForBars->GetSizeAtBarIndex(i);
            for (int VAPIndex = 0; VAPIndex < VAPSizeAtBarIndex; ++VAPIndex)
            {
                const s_VolumeAtPriceV2 *p_VolumeAtPrice = NULL;
                if (sc.VolumeAtPriceForBars->GetVAPElementAtIndex(i, VAPIndex, &p_VolumeAtPrice))
                {
                    if (p_VolumeAtPrice->PriceInTicks * sc.TickSize == price)
                    {
                        if (DISPLAY_DELTA)
                        {
                            // For delta, we need to separate bid and ask volume
                            int bidVolume = p_VolumeAtPrice->BidVolume;
                            int askVolume = p_VolumeAtPrice->AskVolume;
                            currentProfile.deltaVolumeAtPrice[price] += askVolume - bidVolume; // Delta = Ask - Bid
                        }
                        else
                        {
                            // For total volume
                            currentProfile.totalVolumeAtPrice[price] += p_VolumeAtPrice->Volume;
                        }
                        break;
                    }
                }
            }
        }
    }

            // Drawing code for volume profile
    n_ACSIL::s_GraphicsColor positiveDeltaColor;
    positiveDeltaColor.SetRGB(0, 0, 255); // Blue for positive delta

    n_ACSIL::s_GraphicsColor negativeDeltaColor;
    negativeDeltaColor.SetRGB(255, 0, 0); // Red for negative delta

    n_ACSIL::s_GraphicsColor totalVolumeColor;
    totalVolumeColor.SetRGB(100, 0, 255); // Purple for total volume

    int rightEdge = sc.StudyRegionRightCoordinate - 5;

    int maxVolume = 0;
    std::map<float, int>* volumeMap = DISPLAY_DELTA ? &currentProfile.deltaVolumeAtPrice : &currentProfile.totalVolumeAtPrice;
    for (const auto& pair : *volumeMap)
    {
        int volume = abs(pair.second); // Use absolute value for delta to ensure positive width
        if (volume > maxVolume) maxVolume = volume;
    }

    float lastYPos = -1;
    for (auto price : currentProfile.prices)
    {
        int volumeAtPrice = (*volumeMap)[price];

        int y_pos = sc.RegionValueToYPixelCoordinate(price, sc.GraphRegion);

        sc.Graphics.ResetBrush();
        sc.Graphics.ResetPen();

        n_ACSIL::s_GraphicsBrush GraphicsBrush;
        GraphicsBrush.m_BrushType = n_ACSIL::s_GraphicsBrush::BRUSH_TYPE_SOLID;
        if (DISPLAY_DELTA)
        {
            if (volumeAtPrice > 0)
            {
                // Positive delta (buy volume > sell volume)
                GraphicsBrush.m_BrushColor = positiveDeltaColor;
            }
            else if (volumeAtPrice < 0)
            {
                // Negative delta (sell volume > buy volume)
                GraphicsBrush.m_BrushColor = negativeDeltaColor;
            }
        }
        else
        {
            // Total volume
            GraphicsBrush.m_BrushColor = totalVolumeColor;
        }
        sc.Graphics.SetBrush(GraphicsBrush);

        float scaleFactor = 100.0f / maxVolume;
        int rectangleLength = static_cast<int>(abs(volumeAtPrice) * VOLUME_SCALE * scaleFactor); // Use absolute value for delta
        rectangleLength = rectangleLength > 1 ? rectangleLength : 1;

        int heightAdjustment = lastYPos == -1 ? 0 : (y_pos - lastYPos);
        
        // Draw the rectangle on the left side of the profile line for both positive and negative delta
        sc.Graphics.DrawRectangle(rightEdge - rectangleLength, y_pos - (heightAdjustment / 2),
                                  rightEdge, y_pos + (heightAdjustment / 2));

        lastYPos = y_pos;
    }

        // Draw the anchor point instead of a line
    n_ACSIL::s_GraphicsPen anchorPen;
    anchorPen.m_PenColor.SetRGB(255, 0, 0); 
    anchorPen.m_PenStyle = n_ACSIL::s_GraphicsPen::e_PenStyle::PEN_STYLE_SOLID;
    anchorPen.m_Width = 5; // Set width to make it more visible as a dot
    sc.Graphics.SetPen(anchorPen);

    int anchorXPos = sc.BarIndexToXPixelCoordinate(currentProfile.anchorBarIndex); // X position from bar index
    int anchorYPos = sc.RegionValueToYPixelCoordinate(currentProfile.anchorPrice, sc.GraphRegion); // Y position from price

    sc.Graphics.MoveTo(anchorXPos, anchorYPos);
    sc.Graphics.LineTo(anchorXPos + 1, anchorYPos); // Drawing a very short line to create a dot
}