#include "sierrachart.h"
#include <unordered_map>
#include <chrono>

SCDLLName("Sweep Genie")

struct TradeInfo
{
    std::chrono::time_point<std::chrono::steady_clock> startTime;
    double startPrice;
    bool tradeEntered;
    int positionType; // 1 for buy, -1 for sell
    bool stopMovedToBreakeven; // To track if stop loss has been moved to breakeven
    bool trailingStopActivated; // To track if trailing stop has been activated
    int stopOrderID; // To store the stop order ID
};

std::unordered_map<int, TradeInfo> g_TradeInfo;

void PlaceBracketOrder(SCStudyInterfaceRef& sc, TradeInfo& tradeInfo, int positionType, int orderQuantity, double price, int stopLossTicks, int takeProfitTicks)
{
    s_SCNewOrder order;
    order.OrderType = SCT_ORDERTYPE_MARKET;
    order.OrderQuantity = orderQuantity;
    order.TimeInForce = SCT_TIF_GTC;

    // Define the attached orders
    order.Target1Offset = takeProfitTicks * sc.TickSize; // User-defined profit target
    order.Stop1Offset = stopLossTicks * sc.TickSize; // User-defined stop loss

    // Set attached order types
    order.AttachedOrderTarget1Type = SCT_ORDERTYPE_LIMIT;
    order.AttachedOrderStop1Type = SCT_ORDERTYPE_STOP;

    int result = (positionType == 1) ? sc.BuyEntry(order) : sc.SellEntry(order);
    if (result > 0)
    {
        tradeInfo.startTime = std::chrono::steady_clock::now();
        tradeInfo.startPrice = price;
        tradeInfo.tradeEntered = true;
        tradeInfo.positionType = positionType;
        tradeInfo.stopMovedToBreakeven = false; // Initialize to false when trade is entered
        tradeInfo.trailingStopActivated = false; // Initialize to false when trade is entered

        // Store the stop order ID
        tradeInfo.stopOrderID = order.Stop1InternalOrderID;
    }
}

SCSFExport scsf_SweepGenie(SCStudyInterfaceRef sc)
{
    if (sc.SetDefaults)
    {
        sc.GraphName = "Sweep Genie";
        sc.AutoLoop = 1;
        
        sc.Input[0].Name = "Volume Threshold";
        sc.Input[0].SetInt(1000); // Volume Threshold

        sc.Input[1].Name = "Order Quantity";
        sc.Input[1].SetInt(1); // Order Quantity

        sc.Input[2].Name = "Stop Loss Ticks";
        sc.Input[2].SetInt(10); // Stop Loss Ticks

        sc.Input[3].Name = "Take Profit Ticks";
        sc.Input[3].SetInt(20); // Take Profit Ticks

        sc.Input[4].Name = "Recent Volume Threshold";
        sc.Input[4].SetInt(1000); // Recent Volume Threshold

        sc.Input[5].Name = "Trailing Stop Offset Ticks";
        sc.Input[5].SetInt(5); // Trailing Stop Offset Ticks

        return;
    }

    int volumeThreshold = sc.Input[0].GetInt();
    int orderQuantity = sc.Input[1].GetInt();
    int stopLossTicks = sc.Input[2].GetInt();
    int takeProfitTicks = sc.Input[3].GetInt();
    int recentVolumeThreshold = sc.Input[4].GetInt();
    int trailingStopOffsetTicks = sc.Input[5].GetInt();
    
    int maxPositionsAllowed = 1; // This can be an input as well if you want to customize

    SCDateTime currentTime = sc.CurrentSystemDateTime;

    auto& tradeInfo = g_TradeInfo[sc.ChartNumber];

    // Get the current positions
    s_SCPositionData positionData;
    sc.GetTradePosition(positionData);
    int currentPosition = positionData.PositionQuantity;

    double currentPrice = sc.Close[sc.ArraySize - 1];
    
    bool buyConditionMet = false;
    bool sellConditionMet = false;

    // Get pulling/stacking values
    double bidPullingStacking = sc.GetBidMarketDepthStackPullValueAtPrice(currentPrice);
    double askPullingStacking = sc.GetAskMarketDepthStackPullValueAtPrice(currentPrice);

    // Get recent volumes
    double recentBidVolume = sc.BaseData[SC_BIDVOL][sc.ArraySize - 1];
    double recentAskVolume = sc.BaseData[SC_ASKVOL][sc.ArraySize - 1];

    // Checking conditions for Buy Entry
    if (bidPullingStacking >= volumeThreshold)
    {
        // Basic conditions for a Buy
        if (recentAskVolume > recentBidVolume)
        {
            buyConditionMet = true;
        }
    }

    // Checking conditions for Sell Entry
    if (askPullingStacking >= volumeThreshold)
    {
        // Basic conditions for a Sell
        if (recentBidVolume > recentAskVolume)
        {
            sellConditionMet = true;
        }
    }

    // Execute Buy or Sell based on conditions
    if (buyConditionMet && currentPosition < maxPositionsAllowed)
    {
        PlaceBracketOrder(sc, tradeInfo, 1, orderQuantity, currentPrice, stopLossTicks, takeProfitTicks);
    }
    else if (sellConditionMet && -currentPosition < maxPositionsAllowed)
    {
        PlaceBracketOrder(sc, tradeInfo, -1, orderQuantity, currentPrice, stopLossTicks, takeProfitTicks);
    }

    // Exit conditions and trailing stop management
    if (tradeInfo.tradeEntered)
    {
        double stopLossPrice = tradeInfo.startPrice - (tradeInfo.positionType * stopLossTicks * sc.TickSize);
        double takeProfitPrice = tradeInfo.startPrice + (tradeInfo.positionType * takeProfitTicks * sc.TickSize);
        double trailingStopTriggerPrice = tradeInfo.startPrice + (tradeInfo.positionType * takeProfitTicks * 0.7 * sc.TickSize); // 70% of take profit

        if ((tradeInfo.positionType == 1 && (currentPrice <= stopLossPrice || currentPrice >= takeProfitPrice)) ||
            (tradeInfo.positionType == -1 && (currentPrice >= stopLossPrice || currentPrice <= takeProfitPrice)))
        {
            sc.FlattenAndCancelAllOrders();
            tradeInfo.tradeEntered = false;
            tradeInfo.positionType = 0;
        }
        else if (!tradeInfo.trailingStopActivated && tradeInfo.stopOrderID > 0 && 
                 ((tradeInfo.positionType == 1 && currentPrice >= trailingStopTriggerPrice) ||
                  (tradeInfo.positionType == -1 && currentPrice <= trailingStopTriggerPrice)))
        {
            tradeInfo.trailingStopActivated = true;

            s_SCNewOrder modifyOrder;
            modifyOrder.InternalOrderID = tradeInfo.stopOrderID;
            modifyOrder.Price1 = tradeInfo.startPrice + (tradeInfo.positionType * trailingStopOffsetTicks * sc.TickSize);

            int modifyResult = sc.ModifyOrder(modifyOrder);
            if (modifyResult == 1)
            {
                SCString message;
                message.Format("Trailing stop activated at price: %f", modifyOrder.Price1);
                sc.AddMessageToLog(message, 0);
            }
            else
            {
                SCString message;
                message.Format("Failed to modify stop order for trailing stop. Error: %d", modifyResult);
                sc.AddMessageToLog(message, 1);
            }
        }
    }
}
