#include "sierrachart.h"

typedef std::vector<float> t_FloatVector;

void TradingSpreadOrderEntry_DrawStatusText(SCStudyInterfaceRef sc);
void TradingSpreadOrderEntry_UpdateTargetStopSubgraphs(SCStudyInterfaceRef sc, int BeginIndex, int EndIndex);


/*==========================================================================*/
SCSFExport scsf_TradingSpreadOrderEntry(SCStudyInterfaceRef sc)
{
    enum StudyStateEnum : int32_t
    {
        STATE_UNSET = 0
        , STATE_DISABLED = 1
        , STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER = 2
        , STATE_MONITORING_FOR_TARGET_AND_STOP = 3
        , STATE_DISABLED_AFTER_ORDERS_SENT = 4
        , STATE_DISABLED_INVALID_ENTRY_PRICE = 5
    };

    enum SpreadComparisonPriceOperatorEnum : int32_t
    {
        LESS_EQUAL = 0
        , GREATER_EQUAL = 1
    };

    SCSubgraphRef Subgraph_SpreadOrderPrice = sc.Subgraph[0];
    SCSubgraphRef Subgraph_OnChartTextDisplay = sc.Subgraph[1];
    SCSubgraphRef Subgraph_EntryPriceLine = sc.Subgraph[2];
    SCSubgraphRef Subgraph_TargetPriceLine = sc.Subgraph[3];
    SCSubgraphRef Subgraph_StopPriceLine = sc.Subgraph[4];

    SCInputRef Input_IsActiveAndWaitingForSpreadTriggerPrice = sc.Input[0];
    SCInputRef Input_Symbol1Symbol = sc.Input[1];
    SCInputRef Input_Symbol1Side = sc.Input[2];
    SCInputRef Input_Symbol1Quantity = sc.Input[3];

    SCInputRef Input_Symbol2Symbol = sc.Input[4];
    SCInputRef Input_Symbol2Side = sc.Input[5];
    SCInputRef Input_Symbol2Quantity = sc.Input[6];

    SCInputRef Input_Symbol3Symbol = sc.Input[7];
    SCInputRef Input_Symbol3Side = sc.Input[8];
    SCInputRef Input_Symbol3Quantity = sc.Input[9];

    SCInputRef Input_Symbol4Symbol = sc.Input[10];
    SCInputRef Input_Symbol4Side = sc.Input[11];
    SCInputRef Input_Symbol4Quantity = sc.Input[12];

    SCInputRef Input_SpreadEntryPrice = sc.Input[13];
    SCInputRef Input_SpreadEntryPriceOperator = sc.Input[14];

    SCInputRef Input_NumberOfBarsForSubgraphs = sc.Input[15];
    SCInputRef Input_DrawZeroValues = sc.Input[16];

    SCInputRef Input_UseTargetAfterFillOfSpreadEntry = sc.Input[17];
    SCInputRef Input_TargetSpreadPrice = sc.Input[18];

    SCInputRef Input_UseStopAfterFillOfSpreadEntry = sc.Input[19];
    SCInputRef Input_StopSpreadPrice = sc.Input[20];

    SCInputRef Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet = sc.Input[21];
    SCInputRef Input_UseFullContractValuesForSpreadPrices = sc.Input[22];

    if (sc.SetDefaults)
    {
        sc.GraphName = "Trading: Spread Order Entry";
        sc.AutoLoop = 0;//Manual looping
        sc.GraphRegion = 1;
        sc.ScaleRangeType= SCALE_AUTO;
        sc.ValueFormat = VALUEFORMAT_INHERITED;
        sc.UpdateAlways = 1;
        sc.ReceivePointerEvents = ACS_RECEIVE_POINTER_EVENTS_ALWAYS;

        Subgraph_SpreadOrderPrice.Name = "Spread Price";
        Subgraph_SpreadOrderPrice.DrawStyle = DRAWSTYLE_LINE;
        Subgraph_SpreadOrderPrice.PrimaryColor = RGB(0,128,255);
        Subgraph_SpreadOrderPrice.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE| LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER;
        Subgraph_SpreadOrderPrice.DrawZeros = true;

        Subgraph_OnChartTextDisplay.Name = "Chart Text Display";
        Subgraph_OnChartTextDisplay.DrawStyle = DRAWSTYLE_CUSTOM_TEXT;
        Subgraph_OnChartTextDisplay.PrimaryColor = RGB(255, 165, 0);
        Subgraph_OnChartTextDisplay.LineWidth = 10;

        Subgraph_EntryPriceLine.Name = "Entry Price";
        Subgraph_EntryPriceLine.DrawStyle = DRAWSTYLE_LINE;
        Subgraph_EntryPriceLine.LineWidth = 2;
        Subgraph_EntryPriceLine.PrimaryColor = RGB(0, 255, 128);
        Subgraph_EntryPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER;
        Subgraph_EntryPriceLine.DrawZeros = true;

        Subgraph_TargetPriceLine.Name = "Target Price";
        Subgraph_TargetPriceLine.DrawStyle = DRAWSTYLE_LINE;
        Subgraph_TargetPriceLine.LineWidth = 2;
        Subgraph_TargetPriceLine.PrimaryColor = RGB(255, 100, 0);
        Subgraph_TargetPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER;
        Subgraph_TargetPriceLine.DrawZeros = true;

        Subgraph_StopPriceLine.Name = "Stop Price";
        Subgraph_StopPriceLine.DrawStyle = DRAWSTYLE_LINE;
        Subgraph_StopPriceLine.LineWidth = 2;
        Subgraph_StopPriceLine.PrimaryColor = RGB(255, 100, 0);
        Subgraph_StopPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER;
        Subgraph_StopPriceLine.DrawZeros = true;

        Input_IsActiveAndWaitingForSpreadTriggerPrice.Name = "Is Active and Waiting for Spread Trigger Price";
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);

        Input_Symbol1Symbol.Name = "Leg 1 Symbol";
        Input_Symbol1Symbol.SetSymbol("");

        Input_Symbol1Side.Name = "Leg 1 Side";
        Input_Symbol1Side.SetCustomInputStrings("Buy;Sell");
        Input_Symbol1Side.SetCustomInputIndex(0);

        Input_Symbol1Quantity.Name = "Leg 1 Quantity";
        Input_Symbol1Quantity.SetInt(1);

        Input_Symbol2Symbol.Name = "Leg 2 Symbol";
        Input_Symbol2Symbol.SetSymbol("");

        Input_Symbol2Side.Name = "Leg 2 Side";
        Input_Symbol2Side.SetCustomInputStrings("Buy;Sell");
        Input_Symbol2Side.SetCustomInputIndex(1);

        Input_Symbol2Quantity.Name = "Leg 2 Quantity";
        Input_Symbol2Quantity.SetInt(1);

        Input_Symbol3Symbol.Name = "Leg 3 Symbol";
        Input_Symbol3Symbol.SetSymbol("");

        Input_Symbol3Side.Name = "Leg 3 Side";
        Input_Symbol3Side.SetCustomInputStrings("Buy;Sell");
        Input_Symbol3Side.SetCustomInputIndex(0);

        Input_Symbol3Quantity.Name = "Leg 3 Quantity";
        Input_Symbol3Quantity.SetInt(0);

        Input_Symbol4Symbol.Name = "Leg 4 Symbol";
        Input_Symbol4Symbol.SetSymbol("");

        Input_Symbol4Side.Name = "Leg 4 Side";
        Input_Symbol4Side.SetCustomInputStrings("Buy;Sell");
        Input_Symbol4Side.SetCustomInputIndex(0);

        Input_Symbol4Quantity.Name = "Leg 4 Quantity";
        Input_Symbol4Quantity.SetInt(0);

        Input_SpreadEntryPrice.Name = "Spread Entry Price";
        Input_SpreadEntryPrice.SetFloat(0);

        Input_SpreadEntryPriceOperator.Name = "Spread Entry Price Operator";
        Input_SpreadEntryPriceOperator.SetCustomInputStrings("Less or Equal;Greater or Equal");
        Input_SpreadEntryPriceOperator.SetCustomInputIndex(LESS_EQUAL);

        Input_NumberOfBarsForSubgraphs.Name = "Number of Bars for Subgraphs";
        Input_NumberOfBarsForSubgraphs.SetInt(200);
        Input_NumberOfBarsForSubgraphs.SetIntLimits(1, 2000);

        Input_DrawZeroValues.Name = "Draw Zero Values";
        Input_DrawZeroValues.SetYesNo(false);

        Input_UseTargetAfterFillOfSpreadEntry.Name = "Use Target after Fill of Spread Entry";
        Input_UseTargetAfterFillOfSpreadEntry.SetYesNo(false);

        Input_TargetSpreadPrice.Name = "Target Spread Price";
        Input_TargetSpreadPrice.SetFloat(0);

        Input_UseStopAfterFillOfSpreadEntry.Name = "Use Stop after Fill of Spread Entry";
        Input_UseStopAfterFillOfSpreadEntry.SetYesNo(false);

        Input_StopSpreadPrice.Name = "Stop Spread Price";
        Input_StopSpreadPrice.SetFloat(0);

        Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.Name = "Disable if Immediate Fill of Spread After Spread Price Set";
        Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.SetYesNo(false);

        Input_UseFullContractValuesForSpreadPrices.Name = "Use Full Contract Values for Spread Prices";
        Input_UseFullContractValuesForSpreadPrices.SetYesNo(false);

        sc.AllowMultipleEntriesInSameDirection = 1;
        sc.AllowOppositeEntryWithOpposingPositionOrOrders = 1;
        sc.CancelAllOrdersOnEntriesAndReversals = 0;
        sc.AllowOnlyOneTradePerBar = 0;
        sc.SupportReversals = 0;
        sc.AllowEntryWithWorkingOrders = 1;
        sc.SupportAttachedOrdersForTrading = 0;
        sc.UseGUIAttachedOrderSetting = 0;
        sc.MaximumPositionAllowed = 100000000;


        return;
    }

    sc.DisplayAsMainPriceGraph = false;

    int& r_EnableSpreadOrderEntryMenuID = sc.GetPersistentInt(1);
    int& r_DisableSpreadOrderEntryMenuID = sc.GetPersistentInt(2);
    int& r_DisabledActiveStatus = sc.GetPersistentInt(3);
    int& r_CurrentState = sc.GetPersistentInt(4);
    int &r_DrawingNumber = sc.GetPersistentInt(5);
    int& r_OrderModifyMode = sc.GetPersistentInt(6);

    //will both be 0 upon opening the Chartbook or first-time study added
    double& r_PriorSpreadEntryPrice = sc.GetPersistentDouble(1);
    int& r_SpreadEntryPriceHasChanged = sc.GetPersistentInt(7);

    SCString& r_CurrentOnScreenMessage = sc.GetPersistentSCString(1);

    const int PersistentHistoricalSpreadDataKey = 1;
    t_FloatVector* p_PersistentHistoricalSpreadData = reinterpret_cast<t_FloatVector*>(sc.GetPersistentPointer(PersistentHistoricalSpreadDataKey));

    if (sc.LastCallToFunction)
    {
        // Be sure to remove the menu commands when study is removed
        sc.RemoveACSChartShortcutMenuItem(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID);
        sc.RemoveACSChartShortcutMenuItem(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID);

        if (p_PersistentHistoricalSpreadData != NULL)
        {
            delete p_PersistentHistoricalSpreadData;
            sc.SetPersistentPointer(PersistentHistoricalSpreadDataKey, NULL);
        }

        return;
    }

    if (p_PersistentHistoricalSpreadData == NULL)
    {
        p_PersistentHistoricalSpreadData = new t_FloatVector;

        if (p_PersistentHistoricalSpreadData != NULL)
            sc.SetPersistentPointer(PersistentHistoricalSpreadDataKey, p_PersistentHistoricalSpreadData);

    }

    if (p_PersistentHistoricalSpreadData == NULL)
        return;

    t_FloatVector& r_PersistentHistoricalSpreadData = *p_PersistentHistoricalSpreadData;
    r_PersistentHistoricalSpreadData.resize(Input_NumberOfBarsForSubgraphs.GetInt());


    // this will be true when study is added to chart, and when Chartbook is opened with study on chart. This is done as a safety.
    if (r_DisabledActiveStatus == 0)
    {
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);
        r_CurrentState = STATE_DISABLED;
        r_DisabledActiveStatus = 1;
    }

    sc.SendOrdersToTradeService = sc.GlobalTradeSimulationIsOn ? 0 : 1;

    //sc.SendOrdersToTradeService = 0;// TEMPORARY: Safety during development and testing

    if (sc.IsFullRecalculation)
    {
        r_DrawingNumber = 0;

        if (Input_NumberOfBarsForSubgraphs.GetInt() == 0)
            Input_NumberOfBarsForSubgraphs.SetInt(100);

        Subgraph_SpreadOrderPrice.DrawZeros = Input_DrawZeroValues.GetBoolean();
        Subgraph_EntryPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean();

        if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_TargetPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean();
        else
            Subgraph_TargetPriceLine.DrawZeros = false;

        if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_StopPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean();
        else
            Subgraph_StopPriceLine.DrawZeros = false;

        if (r_EnableSpreadOrderEntryMenuID <= 0)
        {
            r_EnableSpreadOrderEntryMenuID = sc.AddACSChartShortcutMenuItem(sc.ChartNumber, "Enable Spread Order Entry");
        }

        if (r_DisableSpreadOrderEntryMenuID <= 0)
        {
            r_DisableSpreadOrderEntryMenuID = sc.AddACSChartShortcutMenuItem(sc.ChartNumber, "Disable Spread Order Entry");
        }

        if (r_CurrentState == STATE_UNSET)
            r_CurrentState = STATE_DISABLED;

    }

    if (Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo())
    {
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 0);
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 1);
    }
    else
    {
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 1);
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 0);
    }

    if (!sc.ChartTradeModeEnabled)
    {
        r_CurrentOnScreenMessage = "Chart Trade Mode is not Enabled";
        TradingSpreadOrderEntry_DrawStatusText(sc);
        return;
    }

    // handle set trigger price menu clicks
    if (sc.MenuEventID != 0 && sc.MenuEventID == r_EnableSpreadOrderEntryMenuID)
    {
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(true);

        if (r_CurrentState == STATE_DISABLED
            || r_CurrentState == STATE_DISABLED_AFTER_ORDERS_SENT
            || r_CurrentState == STATE_DISABLED_INVALID_ENTRY_PRICE)
        {
            r_CurrentState = STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER;
            r_CurrentOnScreenMessage = "Monitoring for spread entry price";
            TradingSpreadOrderEntry_DrawStatusText(sc);
        }

        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 0);
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 1);


    }
    else if (sc.MenuEventID != 0 && sc.MenuEventID == r_DisableSpreadOrderEntryMenuID)
    {
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);
        r_CurrentState = STATE_DISABLED;
        r_CurrentOnScreenMessage = "Disabled";
        TradingSpreadOrderEntry_DrawStatusText(sc);

        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 1);
        sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 0);
    }

    const int NUM_SPREAD_ORDERS = 4;

    SCString SpreadSymbols[NUM_SPREAD_ORDERS];
    int32_t SpreadSides[NUM_SPREAD_ORDERS];
    uint32_t SpreadQuantities[NUM_SPREAD_ORDERS];

    SpreadSymbols[0] = Input_Symbol1Symbol.GetSymbol();
    SpreadSymbols[1] = Input_Symbol2Symbol.GetSymbol();
    SpreadSymbols[2] = Input_Symbol3Symbol.GetSymbol();
    SpreadSymbols[3] = Input_Symbol4Symbol.GetSymbol();

    SpreadSides[0] = Input_Symbol1Side.GetIndex();
    SpreadSides[1] = Input_Symbol2Side.GetIndex();
    SpreadSides[2] = Input_Symbol3Side.GetIndex();
    SpreadSides[3] = Input_Symbol4Side.GetIndex();

    SpreadQuantities[0] = Input_Symbol1Quantity.GetInt();
    SpreadQuantities[1] = Input_Symbol2Quantity.GetInt();
    SpreadQuantities[2] = Input_Symbol3Quantity.GetInt();
    SpreadQuantities[3] = Input_Symbol4Quantity.GetInt();


    if (SpreadSymbols[0].IsEmpty()  || SpreadSymbols[1].IsEmpty()
        || SpreadQuantities[0] == 0 || SpreadQuantities[1] == 0)
    {
        r_CurrentOnScreenMessage = "Settings not complete for spread entry";
        TradingSpreadOrderEntry_DrawStatusText(sc);
        return;
    }

    const bool UseFullContractValues = Input_UseFullContractValuesForSpreadPrices.GetYesNo();

    double CurrentSpreadValue = 0.0;
    bool IsSpreadPriceValid = true;

    int NumberOfPricesInCalculation = 0;
    int NumberOfLegs = 0;
    int NumberOfBuys = 0;
    int NumberOfSells = 0;

    for (int SpreadOrderDataIndex = 0; SpreadOrderDataIndex < NUM_SPREAD_ORDERS; ++SpreadOrderDataIndex )
    {

        if (SpreadQuantities[SpreadOrderDataIndex] == 0.0)
            continue;

        if (SpreadSymbols[SpreadOrderDataIndex].IsEmpty())
            continue;

        ++NumberOfLegs;

        s_SCBasicSymbolData BasicSymbolData;
        sc.GetBasicSymbolData(SpreadSymbols[SpreadOrderDataIndex].GetChars(), BasicSymbolData, true);


        if (BasicSymbolData.Bid == 0.0f || BasicSymbolData.Ask == 0.0f)
        {
            r_CurrentOnScreenMessage = "Bid or Ask prices are zero";
            TradingSpreadOrderEntry_DrawStatusText(sc);
            IsSpreadPriceValid = false;
        }

        if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy
        {
            const float AskPrice = BasicSymbolData.Ask * BasicSymbolData.DisplayPriceMultiplier;
            const double IndividualSymbolValue = AskPrice * SpreadQuantities[SpreadOrderDataIndex];

            if (UseFullContractValues && BasicSymbolData.CurrencyValuePerTick > 0)
            {
                CurrentSpreadValue += (IndividualSymbolValue / BasicSymbolData.TickSize * BasicSymbolData.CurrencyValuePerTick);
            }
            else
                CurrentSpreadValue += IndividualSymbolValue;

            ++NumberOfPricesInCalculation;
            NumberOfBuys++;
        }
        else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell
        {
            const float BidPrice = BasicSymbolData.Bid * BasicSymbolData.DisplayPriceMultiplier;
            const double IndividualSymbolValue = BidPrice * SpreadQuantities[SpreadOrderDataIndex];

            if (UseFullContractValues && BasicSymbolData.CurrencyValuePerTick > 0)
            {
                CurrentSpreadValue -= (IndividualSymbolValue / BasicSymbolData.TickSize * BasicSymbolData.CurrencyValuePerTick);
            }
            else
                CurrentSpreadValue -= IndividualSymbolValue;

            ++NumberOfPricesInCalculation;
            NumberOfSells++;
        }
    }



    double RegionHigh = 0.0;
    double RegionLow = 0.0;

    sc.GetGraphVisibleHighAndLow(RegionHigh, RegionLow);

    bool AllowModify = !((RegionHigh == 0.0 && RegionLow == 0.0)
        || sc.ActiveToolYValue > RegionHigh
        || sc.ActiveToolYValue < RegionLow);

    if (!AllowModify)
        r_OrderModifyMode = 0;

    if (AllowModify && Input_UseTargetAfterFillOfSpreadEntry.GetYesNo()
        && r_OrderModifyMode != 2 && r_OrderModifyMode != 3)//Not modifying Stop or Entry
    {
        double TargetPrice = Input_TargetSpreadPrice.GetDouble();
        int YPosition = sc.RegionValueToYPixelCoordinate(static_cast<float>(TargetPrice), sc.GraphRegion);

        if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN)
        {
            if(YPosition >= sc.PointerVertWindowCoord - 5
                && YPosition <= sc.PointerVertWindowCoord + 5)
                r_OrderModifyMode = 1;
        }

        if (sc.PointerEventType == SC_POINTER_BUTTON_UP)
            r_OrderModifyMode = 0;

        if (r_OrderModifyMode == 1 && sc.PointerEventType == SC_POINTER_MOVE)
        {
            Input_TargetSpreadPrice.SetFloat(static_cast<float>(sc.RoundToTickSize(sc.ActiveToolYValue)));
        }
    }

    if (AllowModify && Input_UseStopAfterFillOfSpreadEntry.GetYesNo()
        && r_OrderModifyMode != 1 && r_OrderModifyMode != 3)
    {
        double StopPrice = Input_StopSpreadPrice.GetDouble();
        int Yposition = sc.RegionValueToYPixelCoordinate(static_cast<float>(StopPrice), sc.GraphRegion);

        if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN)
        {
            if (Yposition >= sc.PointerVertWindowCoord - 5
                && Yposition <= sc.PointerVertWindowCoord + 5)
                r_OrderModifyMode = 2;
        }

        if (sc.PointerEventType == SC_POINTER_BUTTON_UP)
            r_OrderModifyMode = 0;

        if (r_OrderModifyMode == 2 && sc.PointerEventType == SC_POINTER_MOVE)
        {
            Input_StopSpreadPrice.SetFloat(static_cast<float>(sc.RoundToTickSize(sc.ActiveToolYValue)));
        }
    }

    if (AllowModify && r_OrderModifyMode != 1 && r_OrderModifyMode != 2)
    {
        double EntryPrice = Input_SpreadEntryPrice.GetDouble();
        int YPosition = sc.RegionValueToYPixelCoordinate(static_cast<float>(EntryPrice), sc.GraphRegion);

        if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN)
        {
            if (YPosition >= sc.PointerVertWindowCoord - 5
                && YPosition <= sc.PointerVertWindowCoord + 5)
                r_OrderModifyMode = 3;
        }

        if (sc.PointerEventType == SC_POINTER_BUTTON_UP)
            r_OrderModifyMode = 0;

        if (r_OrderModifyMode == 3 && sc.PointerEventType == SC_POINTER_MOVE)
        {
            Input_SpreadEntryPrice.SetFloat(static_cast<float>(sc.RoundToTickSize(sc.ActiveToolYValue)));
        }
    }

    //Check here for changes with spread order entry price
    if (r_PriorSpreadEntryPrice != Input_SpreadEntryPrice.GetFloat())
        r_SpreadEntryPriceHasChanged = 1;

    r_PriorSpreadEntryPrice = Input_SpreadEntryPrice.GetFloat();

    int BarStartIndex = sc.UpdateStartIndex;
    int EarliestIndex = sc.ArraySize - Input_NumberOfBarsForSubgraphs.GetInt();
    if (EarliestIndex > BarStartIndex)
    {
        BarStartIndex = EarliestIndex;
    }

    if (r_OrderModifyMode != 0 && BarStartIndex > EarliestIndex)
        BarStartIndex = EarliestIndex;

    // zero values before the bar starting index for the values
    for (int BarIndex = sc.UpdateStartIndex; BarIndex < BarStartIndex; BarIndex++)
    {
        Subgraph_EntryPriceLine[BarIndex] = 0;
        Subgraph_TargetPriceLine[BarIndex] = 0;
        Subgraph_StopPriceLine[BarIndex] = 0;
        Subgraph_SpreadOrderPrice[BarIndex] = 0;
    }

    for (int BarIndex = BarStartIndex; BarIndex < sc.ArraySize; BarIndex++)
    {
        Subgraph_EntryPriceLine[BarIndex] = Input_SpreadEntryPrice.GetFloat();

        if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_TargetPriceLine[BarIndex] = Input_TargetSpreadPrice.GetFloat();

        if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_StopPriceLine[BarIndex] = Input_StopSpreadPrice.GetFloat();

    }

    if (sc.IsFullRecalculation)
    {
        const int VectorSize = static_cast<int>(r_PersistentHistoricalSpreadData.size());

        int OutputIndex = sc.ArraySize - VectorSize;

        for (int Index = 0; Index < VectorSize; ++Index)
        {
            Subgraph_SpreadOrderPrice[OutputIndex] = r_PersistentHistoricalSpreadData[Index];
            ++OutputIndex;
        }
    }
    else if(IsSpreadPriceValid
        && Subgraph_SpreadOrderPrice[sc.ArraySize - 1] != static_cast<float>(CurrentSpreadValue))
    {
        //shift the current spread price values back
        for (int BarIndex = EarliestIndex; BarIndex < sc.ArraySize - 1; BarIndex++)
        {
            Subgraph_SpreadOrderPrice[BarIndex] = Subgraph_SpreadOrderPrice[BarIndex + 1];
        }

        const int VectorSize = static_cast<int>(r_PersistentHistoricalSpreadData.size());
        for (int Index = 0; Index < VectorSize-1; ++Index)
        {
            r_PersistentHistoricalSpreadData[Index] = r_PersistentHistoricalSpreadData[Index + 1];
        }

    }

    if (IsSpreadPriceValid)
    {
        Subgraph_SpreadOrderPrice[sc.ArraySize - 1] = static_cast<float>(CurrentSpreadValue);

        if (!r_PersistentHistoricalSpreadData.empty())
            r_PersistentHistoricalSpreadData.back() = static_cast<float>(CurrentSpreadValue);
    }

    for (int BarIndex = EarliestIndex - 1; BarIndex >= 0; --BarIndex)
    {
        if (Subgraph_SpreadOrderPrice[BarIndex] == 0
            && Subgraph_EntryPriceLine[BarIndex] == 0
            && Subgraph_TargetPriceLine[BarIndex] == 0
            && Subgraph_StopPriceLine[BarIndex] == 0)
            break;

        Subgraph_SpreadOrderPrice[BarIndex] = 0;
        Subgraph_EntryPriceLine[BarIndex] = 0;
        Subgraph_TargetPriceLine[BarIndex] = 0;
        Subgraph_StopPriceLine[BarIndex] = 0;
    }

    if (sc.IsFullRecalculation)//Do not do any spread price evaluation on a full recalculation
    {
        if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER
            || r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP)
        {
            r_CurrentOnScreenMessage = "Waiting to begin monitoring";
            TradingSpreadOrderEntry_DrawStatusText(sc);
        }

        return;
    }

    if (NumberOfPricesInCalculation != NumberOfLegs)
    {
        r_CurrentOnScreenMessage = "Number of prices in calculation != to the number of legs";
        TradingSpreadOrderEntry_DrawStatusText(sc);
        return;
    }

    if (NumberOfLegs == 2 && NumberOfBuys != NumberOfSells)
    {
        r_CurrentOnScreenMessage = "Disabled: Number of buys != number of sells for 2 legs";
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);
        r_CurrentState = STATE_DISABLED;
        TradingSpreadOrderEntry_DrawStatusText(sc);
        return;
    }


    bool UseTargetOrStop = Input_UseTargetAfterFillOfSpreadEntry.GetYesNo() || Input_UseStopAfterFillOfSpreadEntry.GetYesNo();


    if (!Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo()
        || (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP  && !UseTargetOrStop))
    {

        if (r_CurrentState != STATE_DISABLED
            && r_CurrentState != STATE_DISABLED_AFTER_ORDERS_SENT
            && r_CurrentState != STATE_DISABLED_INVALID_ENTRY_PRICE)
            r_CurrentState = STATE_DISABLED;

        if(r_CurrentState != STATE_DISABLED_AFTER_ORDERS_SENT
            && r_CurrentState != STATE_DISABLED_INVALID_ENTRY_PRICE)
            r_CurrentOnScreenMessage = "Disabled";

        TradingSpreadOrderEntry_DrawStatusText(sc);
        return;
    }

    if(r_CurrentState == STATE_DISABLED
        || r_CurrentState == STATE_DISABLED_AFTER_ORDERS_SENT
        || r_CurrentState == STATE_DISABLED_INVALID_ENTRY_PRICE)
    {
        r_CurrentState = STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER;

    }

    if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER)
    {

        r_CurrentOnScreenMessage = "Monitoring for spread entry price";
        TradingSpreadOrderEntry_DrawStatusText(sc);
    }

    if (r_OrderModifyMode != 0)
        return;


    if (!IsSpreadPriceValid)
        return;


    int NumberOfPricesToCompare = 0;
    double SpreadPricesToCompareTo[2] = {};
    SpreadComparisonPriceOperatorEnum ComparisonOperators[2] = {LESS_EQUAL};
    if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER)
    {
        SpreadPricesToCompareTo[0]= Input_SpreadEntryPrice.GetFloat();
        NumberOfPricesToCompare = 1;
        ComparisonOperators[0]
            = static_cast<SpreadComparisonPriceOperatorEnum>(Input_SpreadEntryPriceOperator.GetIndex());
    }
    else if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP)
    {
        // In the case of greater or equal, invert the operator for the exit order that is lower than the entry.

        // In the case of less or equal, invert the operator for the exit order that is higher than the entry.
        if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo())
        {
            SpreadPricesToCompareTo[NumberOfPricesToCompare] = Input_TargetSpreadPrice.GetFloat();

            if (Input_SpreadEntryPriceOperator.GetIndex() == LESS_EQUAL
                && SpreadPricesToCompareTo[NumberOfPricesToCompare] > Input_SpreadEntryPrice.GetFloat())
            {
                ComparisonOperators[NumberOfPricesToCompare] = GREATER_EQUAL;
            }
            else if (Input_SpreadEntryPriceOperator.GetIndex() == GREATER_EQUAL
                && SpreadPricesToCompareTo[NumberOfPricesToCompare] < Input_SpreadEntryPrice.GetFloat())
            {
                ComparisonOperators[NumberOfPricesToCompare] = LESS_EQUAL;
            }
            else
            {
                ComparisonOperators[NumberOfPricesToCompare]
                    = static_cast<SpreadComparisonPriceOperatorEnum>(Input_SpreadEntryPriceOperator.GetIndex());
            }

            NumberOfPricesToCompare++;
        }

        if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo())
        {
            SpreadPricesToCompareTo[NumberOfPricesToCompare] = Input_StopSpreadPrice.GetFloat();

            if (Input_SpreadEntryPriceOperator.GetIndex() == LESS_EQUAL
                && SpreadPricesToCompareTo[NumberOfPricesToCompare] > Input_SpreadEntryPrice.GetFloat())
            {
                ComparisonOperators[NumberOfPricesToCompare] = GREATER_EQUAL;
            }
            else if (Input_SpreadEntryPriceOperator.GetIndex() == GREATER_EQUAL
                && SpreadPricesToCompareTo[NumberOfPricesToCompare] < Input_SpreadEntryPrice.GetFloat())
            {
                ComparisonOperators[NumberOfPricesToCompare] = LESS_EQUAL;
            }
            else
            {
                ComparisonOperators[NumberOfPricesToCompare]
                    = static_cast<SpreadComparisonPriceOperatorEnum>(Input_SpreadEntryPriceOperator.GetIndex());
            }

            NumberOfPricesToCompare++;
        }

    }

    bool SpreadPriceMet = false;
    double SpreadTriggerPrice = 0.0;

    for(int Index = 0; Index  < NumberOfPricesToCompare; Index++)
    {
        if (ComparisonOperators[Index] == LESS_EQUAL)
        {
            if (CurrentSpreadValue <= SpreadPricesToCompareTo[Index])
            {
                if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER)
                    r_CurrentOnScreenMessage = "Triggered | ";
                else
                    r_CurrentOnScreenMessage = "Target/Stop Triggered | ";

                SpreadPriceMet = true;
                SpreadTriggerPrice = CurrentSpreadValue;

                r_CurrentOnScreenMessage += sc.FormatGraphValue(SpreadTriggerPrice, sc.BaseGraphValueFormat);
                r_CurrentOnScreenMessage += " | ";
            }

        }
        else if (ComparisonOperators[Index] == GREATER_EQUAL)
        {
            if (CurrentSpreadValue >= SpreadPricesToCompareTo[Index])
            {
                if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER)
                    r_CurrentOnScreenMessage = "Triggered | ";
                else
                    r_CurrentOnScreenMessage = "Target/Stop Triggered | ";

                SpreadPriceMet = true;
                SpreadTriggerPrice = CurrentSpreadValue;

                r_CurrentOnScreenMessage += sc.FormatGraphValue(SpreadTriggerPrice, sc.BaseGraphValueFormat);
                r_CurrentOnScreenMessage += " | ";
            }

        }
        else
            return;

    }

    if (SpreadPriceMet)
    {
        if (r_SpreadEntryPriceHasChanged
            && Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.GetYesNo())
        {
            r_SpreadEntryPriceHasChanged = 0;

            Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);
            r_CurrentState = STATE_DISABLED_INVALID_ENTRY_PRICE;
            r_CurrentOnScreenMessage = "Disabled: DisableIfImmediateFillOfSpreadAfterSpreadPriceSet";

            TradingSpreadOrderEntry_DrawStatusText(sc);
            return;
        }

        r_SpreadEntryPriceHasChanged = 0;
    }
    else
    {
        return;
    }


    if (!Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo())
        return;//this will not be reached but is here for safety.

    if (r_CurrentState != STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER
        && r_CurrentState != STATE_MONITORING_FOR_TARGET_AND_STOP)
        return;


    //From this point on *must* get to end of function for safety so state will be set to disabled or monitoring for target or stop.


    r_CurrentOnScreenMessage += sc.FormatDateTime(sc.CurrentSystemDateTime);
    r_CurrentOnScreenMessage += ". ";

    bool Successful = false;
    for (int SpreadOrderDataIndex = 0; SpreadOrderDataIndex < NUM_SPREAD_ORDERS; ++SpreadOrderDataIndex)
    {

        if (SpreadQuantities[SpreadOrderDataIndex] == 0.0)
            continue;

        if (SpreadSymbols[SpreadOrderDataIndex].IsEmpty())
            continue;


        if (SpreadOrderDataIndex > 0)
            r_CurrentOnScreenMessage += ", ";

        // At this point it is safe to submit the market order

        s_SCNewOrder NewOrder;
        NewOrder.OrderQuantity = SpreadQuantities[SpreadOrderDataIndex];
        NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
        NewOrder.TimeInForce = SCT_TIF_DAY;

        NewOrder.Symbol = SpreadSymbols[SpreadOrderDataIndex];
        NewOrder.TradeAccount = sc.SelectedTradeAccount;
        NewOrder.TextTag = "Spread order entry leg";

        int Result = 0;
        if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP  && UseTargetOrStop)
        {
            if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy
                Result = static_cast<int>(sc.SellOrder(NewOrder));
            else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell
                Result = static_cast<int>(sc.BuyOrder(NewOrder));
        }
        else
        {
            if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy
                Result = static_cast<int>(sc.BuyOrder(NewOrder));
            else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell
                Result = static_cast<int>(sc.SellOrder(NewOrder));
        }

        if (Result < 0)
        {
            r_CurrentOnScreenMessage = "Submission error: ";
            r_CurrentOnScreenMessage  += sc.GetTradingErrorTextMessage(Result);
        }
        else
        {
            Successful = true;
            r_CurrentOnScreenMessage += NewOrder.Symbol;
            r_CurrentOnScreenMessage += "*";
            if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP  && UseTargetOrStop)
            {
                if (SpreadSides[SpreadOrderDataIndex] == 0)
                    r_CurrentOnScreenMessage += "-";
            }
            else
            {
                if (SpreadSides[SpreadOrderDataIndex] == 1)
                    r_CurrentOnScreenMessage += "-";
            }

            r_CurrentOnScreenMessage.AppendFormat("%0.2f", NewOrder.OrderQuantity);

            SCString MessageText("Market order submitted for spread leg ");
            MessageText += NewOrder.Symbol;
            sc.AddMessageToTradeServiceLog(MessageText, false);

        }

    }

    //Disable or monitor for target or stop

    if (!UseTargetOrStop)
    {
        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable
        r_CurrentOnScreenMessage += ". Not Monitoring";
        r_CurrentState = STATE_DISABLED_AFTER_ORDERS_SENT;

        Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable
    }
    else
    {
        if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER)
        {
            r_CurrentOnScreenMessage += ". Monitoring Target/Stop";
            r_CurrentState = STATE_MONITORING_FOR_TARGET_AND_STOP;
        }
        else
        {
            r_CurrentOnScreenMessage += ". Not Monitoring";
            r_CurrentState = STATE_DISABLED_AFTER_ORDERS_SENT;
            Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable
        }

    }


    TradingSpreadOrderEntry_DrawStatusText(sc);
}

/*==========================================================================*/
void TradingSpreadOrderEntry_DrawStatusText(SCStudyInterfaceRef sc )
{

    SCSubgraphRef Subgraph_OnChartTextDisplay = sc.Subgraph[1];

    int &r_DrawingNumber = sc.GetPersistentInt(5);

    SCString& r_CurrentOnScreenMessage = sc.GetPersistentSCString(1);

    s_UseTool Tool;

    Tool.Clear();
    Tool.ChartNumber = sc.ChartNumber;
    Tool.DrawingType = DRAWING_TEXT;
    Tool.BeginDateTime = 10;
    Tool.Region = sc.GraphRegion;
    Tool.BeginValue = 90;
    Tool.UseRelativeVerticalValues = true;
    Tool.Color = Subgraph_OnChartTextDisplay.PrimaryColor;
    Tool.FontBold = true;

    Tool.Text = r_CurrentOnScreenMessage;
    Tool.FontSize = Subgraph_OnChartTextDisplay.LineWidth;
    Tool.AddMethod = UTAM_ADD_OR_ADJUST;
    Tool.ReverseTextColor = false;

    if (r_DrawingNumber != 0)
        Tool.LineNumber = r_DrawingNumber;

    if(sc.UseTool(Tool))
        r_DrawingNumber = Tool.LineNumber;

}

/*==========================================================================*/
void TradingSpreadOrderEntry_UpdateTargetStopSubgraphs(SCStudyInterfaceRef sc, int BeginIndex, int EndIndex)
{

    SCSubgraphRef Subgraph_TargetPriceLine = sc.Subgraph[3];
    SCSubgraphRef Subgraph_StopPriceLine = sc.Subgraph[4];

    SCInputRef Input_UseTargetAfterFillOfSpreadEntry = sc.Input[17];
    SCInputRef Input_TargetSpreadPrice = sc.Input[18];

    SCInputRef Input_UseStopAfterFillOfSpreadEntry = sc.Input[19];
    SCInputRef Input_StopSpreadPrice = sc.Input[20];

    for (int BarIndex = BeginIndex; BarIndex < EndIndex; BarIndex++)
    {

        if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_TargetPriceLine[BarIndex] = Input_TargetSpreadPrice.GetFloat();

        if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo())
            Subgraph_StopPriceLine[BarIndex] = Input_StopSpreadPrice.GetFloat();

    }
}