#include "sierrachart.h"

SCDLLName("HotkeyStops")

enum OrderSelector { NEAREST_ORDER_ABOVE, NEAREST_ORDER_BELOW };

int GetNearestStopOrder(SCStudyInterfaceRef sc, OrderSelector orderSelector, s_SCTradeOrder &nearestOrder)
{
    s_SCTradeOrder order;

    int i = 0;
    double lastPrice = sc.Close[sc.ArraySize -1];
    double orderDistance = -1;
    double d = -1;
    int nearestOrderId;
    while(sc.GetOrderByIndex(i, order) != SCTRADING_ORDER_ERROR)
    {
        i++;

        if (order.OrderStatusCode != SCT_OSC_OPEN)
            continue;

        if (order.OrderTypeAsInt != SCT_ORDERTYPE_STOP)
            continue;

        if (orderSelector == NEAREST_ORDER_ABOVE && order.Price1 < lastPrice)
            continue;

        if (orderSelector == NEAREST_ORDER_BELOW && order.Price1 > lastPrice)
            continue;

        orderDistance = abs(order.Price1 - lastPrice);

        if (d == -1 || (orderDistance < d))
        {
            d = orderDistance;
            nearestOrderId = order.InternalOrderID;
            nearestOrder = order;
        }

    }

    return nearestOrderId;
}

const char* ModifierList = "None;Shift;Ctrl;Alt;Shift Ctrl;Shift Alt;Ctrl Alt;Shift Ctrl Alt";

bool IsModifiersPressed(SCStudyInterfaceRef sc, int requiredModifierCombinationIndex)
{
    switch (requiredModifierCombinationIndex)
    {
        case 0:
            return ! sc.IsKeyPressed_Shift && ! sc.IsKeyPressed_Control && ! sc.IsKeyPressed_Alt;
        case 1:
            return   sc.IsKeyPressed_Shift && ! sc.IsKeyPressed_Control && ! sc.IsKeyPressed_Alt;
        case 2:
            return ! sc.IsKeyPressed_Shift &&   sc.IsKeyPressed_Control && ! sc.IsKeyPressed_Alt;
        case 3:
            return ! sc.IsKeyPressed_Shift && ! sc.IsKeyPressed_Control &&   sc.IsKeyPressed_Alt;
        case 4:
            return   sc.IsKeyPressed_Shift &&   sc.IsKeyPressed_Control && ! sc.IsKeyPressed_Alt;
        case 5:
            return   sc.IsKeyPressed_Shift && ! sc.IsKeyPressed_Control &&   sc.IsKeyPressed_Alt;
        case 6:
            return ! sc.IsKeyPressed_Shift &&   sc.IsKeyPressed_Control &&   sc.IsKeyPressed_Alt;
        case 7:
            return   sc.IsKeyPressed_Shift &&   sc.IsKeyPressed_Control &&   sc.IsKeyPressed_Alt;
    }
    return false;
}

SCSFExport scsf_OrderManager(SCStudyInterfaceRef sc)
{
    SCInputRef Input_TickUp_NearestAbove_KeyCode = sc.Input[0];
    SCInputRef Input_TickUp_NearestAbove_RequireModifiers = sc.Input[1];

    SCInputRef Input_TickDown_NearestAbove_KeyCode = sc.Input[2];
    SCInputRef Input_TickDown_NearestAbove_RequireModifiers = sc.Input[3];

    SCInputRef Input_TickUp_NearestBelow_KeyCode = sc.Input[4];
    SCInputRef Input_TickUp_NearestBelow_RequireModifiers = sc.Input[5];

    SCInputRef Input_TickDown_NearestBelow_KeyCode = sc.Input[6];
    SCInputRef Input_TickDown_NearestBelow_RequireModifiers = sc.Input[7];

    if (sc.SetDefaults)
    {
        // Set the configuration and defaults

        sc.GraphName = "Order Manager";

        sc.StudyDescription = "Add hotkeys to move Stop Orders.";

        sc.AutoLoop = 0;    //Manual looping
        sc.FreeDLL = 1;     // During development set this flag to 1, so the DLL can be rebuilt without restarting Sierra Chart. When development is completed, set it to 0 to improve performance.

        sc.GraphRegion = 0;

        Input_TickUp_NearestAbove_KeyCode.Name = "Move nearest Buy Stop up one tick keycode";
        Input_TickUp_NearestAbove_KeyCode.SetDescription(
        "To find a keyboard character code, see <a href=\"https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes\">https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes</a>.<br>"
        "Note, if the key is already assigned to a global shortcut, it will be ignored by this study."
        );
        Input_TickUp_NearestAbove_KeyCode.SetInt(124);
        Input_TickUp_NearestAbove_KeyCode.SetIntLimits(1, 254);

        Input_TickUp_NearestAbove_RequireModifiers.Name = "Move nearest Buy Stop up one tick require Modifiers";
        Input_TickUp_NearestAbove_RequireModifiers.SetCustomInputStrings(ModifierList);
        Input_TickUp_NearestAbove_RequireModifiers.SetCustomInputIndex(0);

        Input_TickDown_NearestAbove_KeyCode.Name = "Move nearest Buy Stop down one tick keycode";
        Input_TickDown_NearestAbove_KeyCode.SetDescription(
        "To find a keyboard character code, see <a href=\"https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes\">https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes</a>.<br>"
        "Note, if the key is already assigned to a global shortcut, it will be ignored by this study."
        );
        Input_TickDown_NearestAbove_KeyCode.SetInt(125);
        Input_TickDown_NearestAbove_KeyCode.SetIntLimits(1, 254);

        Input_TickDown_NearestAbove_RequireModifiers.Name = "Move nearest Buy Stop down one tick require Modifiers";
        Input_TickDown_NearestAbove_RequireModifiers.SetCustomInputStrings(ModifierList);
        Input_TickDown_NearestAbove_RequireModifiers.SetCustomInputIndex(0);

        Input_TickUp_NearestBelow_KeyCode.Name = "Move nearest Sell Stop up one tick keycode";
        Input_TickUp_NearestBelow_KeyCode.SetDescription(
        "To find a keyboard character code, see <a href=\"https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes\">https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes</a>.<br>"
        "Note, if the key is already assigned to a global shortcut, it will be ignored by this study."
        );
        Input_TickUp_NearestBelow_KeyCode.SetInt(126);
        Input_TickUp_NearestBelow_KeyCode.SetIntLimits(1, 254);

        Input_TickUp_NearestBelow_RequireModifiers.Name = "Move nearest Sell Stop up one tick require Modifiers";
        Input_TickUp_NearestBelow_RequireModifiers.SetCustomInputStrings(ModifierList);
        Input_TickUp_NearestBelow_RequireModifiers.SetCustomInputIndex(0);

        Input_TickDown_NearestBelow_KeyCode.Name = "Move nearest Sell Stop down one tick keycode";
        Input_TickDown_NearestBelow_KeyCode.SetDescription(
        "To find a keyboard character code, see <a href=\"https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes\">https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes</a>.<br>"
        "Note, if the key is already assigned to a global shortcut, it will be ignored by this study."
        );
        Input_TickDown_NearestBelow_KeyCode.SetInt(127);
        Input_TickDown_NearestBelow_KeyCode.SetIntLimits(1, 254);

        Input_TickDown_NearestBelow_RequireModifiers.Name = "Move nearest Sell Stop down one tick require Shift";
        Input_TickDown_NearestBelow_RequireModifiers.SetCustomInputStrings(ModifierList);
        Input_TickDown_NearestBelow_RequireModifiers.SetCustomInputIndex(0);

        sc.UpdateAlways = 0;    // This study only need to be called on a keypress

        sc.DoNotRedrawChartAfterStudyReturns = 1;   // No changes to charts are made

        sc.ReceiveCharacterEvents = 0;  // Must be 0; if a charEvent is detected then sc.KeyboardKeyEventCode is not updated.
        sc.ReceiveKeyboardKeyEvents  = 1;
        sc.SupportKeyboardModifierStates = 1;

        return;
    }

    // Do nothing if study is hidden
    if (sc.HideStudy)
        return;

    s_SCTradeOrder orderToModify;
    int tickChange = 0;
    if (IsModifiersPressed(sc, Input_TickUp_NearestAbove_RequireModifiers.GetIndex()) && (sc.KeyboardKeyEventCode == Input_TickUp_NearestAbove_KeyCode.GetInt()))
    {
        if (GetNearestStopOrder(sc, NEAREST_ORDER_ABOVE, orderToModify))
            tickChange = +1;
    }

    if (IsModifiersPressed(sc, Input_TickDown_NearestAbove_RequireModifiers.GetIndex()) && (sc.KeyboardKeyEventCode == Input_TickDown_NearestAbove_KeyCode.GetInt()))
    {
        if (GetNearestStopOrder(sc, NEAREST_ORDER_ABOVE, orderToModify))
            tickChange = -1;
    }

    if (IsModifiersPressed(sc, Input_TickUp_NearestBelow_RequireModifiers.GetIndex()) && (sc.KeyboardKeyEventCode == Input_TickUp_NearestBelow_KeyCode.GetInt()))
    {
        if (GetNearestStopOrder(sc, NEAREST_ORDER_BELOW, orderToModify))
            tickChange = +1;
    }

    if (IsModifiersPressed(sc, Input_TickDown_NearestBelow_RequireModifiers.GetIndex()) && (sc.KeyboardKeyEventCode == Input_TickDown_NearestBelow_KeyCode.GetInt()))
    {
        if (GetNearestStopOrder(sc, NEAREST_ORDER_BELOW, orderToModify))
            tickChange = -1;
    }

    if (tickChange != 0)
    {
        s_SCNewOrder ModifyOrder;
        ModifyOrder.InternalOrderID = orderToModify.InternalOrderID;
        ModifyOrder.Price1 = orderToModify.Price1 + (tickChange * sc.TickSize);
        sc.ModifyOrder(ModifyOrder);
    }

}