//+------------------------------------------------------------------------------------------------------------------+
//|                               Copyright © 2020, Gehtsoft USA LLC | 
//|                                            http://fxcodebase.com |
//+------------------------------------------------------------------------------------------------------------------+
//|                                      Developed by : Mario Jemic  |
//|                                          mario.jemic@gmail.com   |
//+------------------------------------------------------------------------------------------------------------------+
//|                                 Support our efforts by donating  |
//|                                  Paypal : https://goo.gl/9Rj74e  |
//+------------------------------------------------------------------------------------------------------------------+
//|                                Patreon : https://goo.gl/GdXWeN  |
//+------------------------------------------------------------------------------------------------------------------+
// not for sale, rent, auction, nor lease
//
//+------------------------------------------------------------------------------------------------------------------+
#property copyright "Alexander Elgin"
#property link      "alexmost113@mail.ru"
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0

#define PERIODS_NUMBER 5
#define INDICATOR_NAME "Fractal Support Resistance"
#define SUPPORT "Support"
#define RESISTANCE "Resistance"
#define BAR_TO_START_SCAN_FROM 2

//+------------------------------------------------------------------------------------------------------------------+
//| Fractal levels settings                                                                                          |
//+------------------------------------------------------------------------------------------------------------------+
input group "=== Fractal Levels ==="
input color               H1Color              = clrGreen;      // H1 line color
input color               H4Color              = clrDodgerBlue; // H4 line color
input color               D1Color              = clrDarkOrange; // D1 line color
input color               W1Color              = clrRed;        // W1 line color
input color               MN1Color             = clrPurple;     // MN1 line color
input ENUM_LINE_STYLE     H1LineStyle          = STYLE_DOT;     // H1 line style
input ENUM_LINE_STYLE     H4LineStyle          = STYLE_DOT;     // H4 line style
input ENUM_LINE_STYLE     D1LineStyle          = STYLE_SOLID;   // D1 line style
input ENUM_LINE_STYLE     W1LineStyle          = STYLE_SOLID;   // W1 line style
input ENUM_LINE_STYLE     MN1LineStyle         = STYLE_SOLID;   // MN1 line style
input int                 H1LineWidth          = 1;             // H1 line width
input int                 H4LineWidth          = 1;             // H4 line width
input int                 D1LineWidth          = 1;             // D1 line width
input int                 W1LineWidth          = 1;             // W1 line width
input int                 MN1LineWidth         = 1;             // MN1 line width

input group "=== Labels ==="
input bool                DisplayTextLabel     = true;          // Display text labels
input int                 LabelFontSize        = 10;            // Label font size
input string              LabelFont            = "Arial";       // Label font
input int                 ShiftLabel           = 10;            // Label shift: + move right, - move left

//Forex-Station copy & paste code; Button code start 11
input group "=== Toggle Button Settings ==="
input string             button_note1          = "------------------------------"; // ------------------------------
input int                btn_Subwindow         = 0;                                // What window to put the button on
input ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER;                // chart btn_corner for anchoring
input string             btn_text              = "FSR: ON";                        // Display label
input string             btn_Font              = "Arial";                          // button font name
input int                btn_FontSize          = 10;                               // btn__font size
input color              btn_text_ON_color     = clrLime;                          // ON color when the button is turned on
input color              btn_text_OFF_color    = clrRed;                           // OFF color when the button is turned off
input color              btn_background_color  = clrDimGray;                       // background color of the button
input color              btn_border_color      = clrBlack;                         // border color the button
input int                button_x              = 20;                               // Horizontal location
input int                button_y              = 80;                               // Vertical location
input int                btn_Width             = 80;                               // btn__width
input int                btn_Height            = 20;                               // btn__height
input string             UniqueButtonID        = "FSR2026mt5";                     // Unique Button ID
input string             button_note2          = "------------------------------"; // ------------------------------
//Forex-Station copy & paste code; Button code end 11

//+------------------------------------------------------------------+
//| Button State Tracking Variables                                  |
//+------------------------------------------------------------------+
bool    IndicatorEnabled = true;
bool    IndicatorHidden  = false; // True when indicator is turned OFF via toggle button
string  ToggleButtonName;
string  g_gvHidden;
string  g_prefix;
string  IndicatorName, IndicatorObjPrefix, buttonId;

//+------------------------------------------------------------------+
//| Fractal levels state                                             |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES timeframes[PERIODS_NUMBER] = {PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1};
int   styles[PERIODS_NUMBER];
color colors[PERIODS_NUMBER];
int   widths[PERIODS_NUMBER];

//+------------------------------------------------------------------+
//| Helper to resolve button text based on state                     |
//+------------------------------------------------------------------+
string GetButtonText(bool isEnabled)
{
   string t = btn_text;
   if(isEnabled)
   {
      if(StringFind(t, "OFF") >= 0) StringReplace(t, "OFF", "ON");
      else if(StringFind(t, "Off") >= 0) StringReplace(t, "Off", "On");
      else if(StringFind(t, "off") >= 0) StringReplace(t, "off", "on");
   }
   else
   {
      if(StringFind(t, "ON") >= 0) StringReplace(t, "ON", "OFF");
      else if(StringFind(t, "On") >= 0) StringReplace(t, "On", "Off");
      else if(StringFind(t, "on") >= 0) StringReplace(t, "on", "off");
   }
   return t;
}

//+------------------------------------------------------------------+
//| Create Toggle Button                                             |
//+------------------------------------------------------------------+
void CreateToggleButton()
{
   if(ObjectFind(0, buttonId) < 0)
   {
      int y_pos = button_y;
      int obj_total = ObjectsTotal(0, -1, OBJ_BUTTON);
      bool collision = true;
      while(collision)
      {
         collision = false;
         for(int i = 0; i < obj_total; i++)
         {
            string name = ObjectName(0, i, -1, OBJ_BUTTON);
            if(name == buttonId) continue;
            if((ENUM_BASE_CORNER)ObjectGetInteger(0, name, OBJPROP_CORNER) != btn_corner) continue;
            if(ObjectGetInteger(0, name, OBJPROP_XDISTANCE) == button_x &&
               ObjectGetInteger(0, name, OBJPROP_YDISTANCE) == y_pos)
            {
               y_pos += btn_Height + 4;
               collision = true;
               break;
            }
         }
      }

      ObjectCreate(0, buttonId, OBJ_BUTTON, btn_Subwindow, 0, 0);

      ObjectSetInteger(0, buttonId, OBJPROP_CORNER,       btn_corner);
      ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE,    button_x);
      ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE,    y_pos);
      ObjectSetInteger(0, buttonId, OBJPROP_XSIZE,        btn_Width);
      ObjectSetInteger(0, buttonId, OBJPROP_YSIZE,        btn_Height);
      ObjectSetString (0, buttonId, OBJPROP_FONT,         btn_Font);
      ObjectSetInteger(0, buttonId, OBJPROP_FONTSIZE,     btn_FontSize);
      ObjectSetInteger(0, buttonId, OBJPROP_BGCOLOR,      btn_background_color);
      ObjectSetInteger(0, buttonId, OBJPROP_BORDER_COLOR, btn_border_color);
      ObjectSetInteger(0, buttonId, OBJPROP_BORDER_TYPE,  BORDER_RAISED);
      ObjectSetInteger(0, buttonId, OBJPROP_SELECTABLE,   false);
      ObjectSetInteger(0, buttonId, OBJPROP_HIDDEN,       true);
      ObjectSetInteger(0, buttonId, OBJPROP_TIMEFRAMES,   OBJ_ALL_PERIODS);
      ObjectSetInteger(0, buttonId, OBJPROP_ZORDER,       100);
   }

   UpdateToggleButton();
}

//+------------------------------------------------------------------+
//| Update Toggle Button Appearance                                  |
//+------------------------------------------------------------------+
void UpdateToggleButton()
{
   if(ObjectFind(0, buttonId) < 0) return;

   IndicatorEnabled = !IndicatorHidden;

   string labelText  = GetButtonText(IndicatorEnabled);
   color  textColor  = IndicatorEnabled ? btn_text_ON_color : btn_text_OFF_color;

   ObjectSetString (0, buttonId, OBJPROP_TEXT,         labelText);
   ObjectSetString (0, buttonId, OBJPROP_FONT,         btn_Font);
   ObjectSetInteger(0, buttonId, OBJPROP_FONTSIZE,     btn_FontSize);
   ObjectSetInteger(0, buttonId, OBJPROP_COLOR,        textColor);
   ObjectSetInteger(0, buttonId, OBJPROP_BGCOLOR,      btn_background_color);
   ObjectSetInteger(0, buttonId, OBJPROP_BORDER_COLOR, btn_border_color);
   ObjectSetInteger(0, buttonId, OBJPROP_CORNER,       btn_corner);
   ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE,    button_x);
   ObjectSetInteger(0, buttonId, OBJPROP_XSIZE,        btn_Width);
   ObjectSetInteger(0, buttonId, OBJPROP_YSIZE,        btn_Height);
   ObjectSetInteger(0, buttonId, OBJPROP_STATE,        IndicatorEnabled);
   ObjectSetInteger(0, buttonId, OBJPROP_ZORDER,       100);

   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Ensure all buttons on chart have higher ZORDER                   |
//+------------------------------------------------------------------+
void BringButtonsToFront()
{
   int total = ObjectsTotal(0, -1, OBJ_BUTTON);
   for(int i = 0; i < total; i++)
   {
      string name = ObjectName(0, i, -1, OBJ_BUTTON);
      if(name != "")
      {
         if(ObjectGetInteger(0, name, OBJPROP_ZORDER) < 100)
            ObjectSetInteger(0, name, OBJPROP_ZORDER, 100);
      }
   }
}

//+------------------------------------------------------------------+
//| Restore toggle state: button text first, then state, then global |
//+------------------------------------------------------------------+
void RestoreToggleState()
{
   bool hasSavedState = false;
   bool savedHidden   = false;

   if(ObjectFind(0, buttonId) >= 0)
   {
      string btnTxt = ObjectGetString(0, buttonId, OBJPROP_TEXT);
      long   btnSt  = ObjectGetInteger(0, buttonId, OBJPROP_STATE);

      if(StringFind(btnTxt, "OFF") >= 0 || StringFind(btnTxt, "Off") >= 0 || btnSt == 0)
         savedHidden = true;
      else if(StringFind(btnTxt, "ON") >= 0 || StringFind(btnTxt, "On") >= 0 || btnSt != 0)
         savedHidden = false;

      hasSavedState = true;
   }

   if(!hasSavedState && GlobalVariableCheck(g_gvHidden))
   {
      savedHidden   = (GlobalVariableGet(g_gvHidden) > 0.5);
      hasSavedState = true;
   }

   IndicatorHidden  = hasSavedState ? savedHidden : false;
   IndicatorEnabled = !IndicatorHidden;

   if(g_gvHidden != "")
      GlobalVariableSet(g_gvHidden, IndicatorHidden ? 1.0 : 0.0);
}

//+------------------------------------------------------------------+
//| Toggle Indicator Visibility                                      |
//+------------------------------------------------------------------+
void ToggleIndicatorVisibility()
{
   IndicatorHidden  = !IndicatorHidden;
   IndicatorEnabled = !IndicatorHidden;

   if(g_gvHidden != "")
      GlobalVariableSet(g_gvHidden, IndicatorHidden ? 1.0 : 0.0);

   if(IndicatorHidden)
      DeleteSRObjects();
   else
      RefreshObjects();

   UpdateToggleButton();
   BringButtonsToFront();
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Helper: Get timeframe as string                                  |
//+------------------------------------------------------------------+
string GetStringTimeframe(ENUM_TIMEFRAMES timeframe)
{
   return(StringSubstr(EnumToString(timeframe), 7));
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Set up unique IDs and global variables for state tracking
   IndicatorObjPrefix = "__" + btn_text + "__";
   buttonId           = IndicatorObjPrefix + UniqueButtonID;
   ToggleButtonName   = buttonId;
   IndicatorName      = "[" + Symbol() + "] [" + GetStringTimeframe(_Period) + "] Fractal Support Resistance";
   g_prefix           = "FSR_BT_" + UniqueButtonID + "_";
   g_gvHidden         = g_prefix + IntegerToString(ChartID()) + "_Hidden";

   IndicatorSetString(INDICATOR_SHORTNAME, IndicatorName);
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);

//--- Restore toggle state (kept across parameter, timeframe and symbol changes)
   RestoreToggleState();
   CreateToggleButton();
   BringButtonsToFront();

//--- put init() here
   DeleteSRObjects();

   colors[0] = H1Color;
   colors[1] = H4Color;
   colors[2] = D1Color;
   colors[3] = W1Color;
   colors[4] = MN1Color;

   styles[0] = H1LineStyle;
   styles[1] = H4LineStyle;
   styles[2] = D1LineStyle;
   styles[3] = W1LineStyle;
   styles[4] = MN1LineStyle;

   widths[0] = H1LineWidth;
   widths[1] = H4LineWidth;
   widths[2] = D1LineWidth;
   widths[3] = W1LineWidth;
   widths[4] = MN1LineWidth;

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(reason == REASON_REMOVE || reason == REASON_CHARTCLOSE || reason == REASON_INITFAILED)
   {
      ObjectDelete(0, buttonId);
      ObjectDelete(0, ToggleButtonName);
      if(g_gvHidden != "")
         GlobalVariableDel(g_gvHidden);
   }
   else if(reason != REASON_CHARTCHANGE && reason != REASON_PARAMETERS && reason != REASON_RECOMPILE)
      ObjectDelete(0, buttonId);

//--- put deinit() here
   DeleteSRObjects();

   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Custom indicator chart's event handler                           |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      string altId = "_" + UniqueButtonID + IndicatorObjPrefix + "_BT_";
      if(sparam == buttonId || sparam == ToggleButtonName || sparam == altId)
      {
         ToggleIndicatorVisibility();
         return;
      }
   }

   if(id == CHARTEVENT_CHART_CHANGE)
      BringButtonsToFront();
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(prev_calculated <= 0)
   {
      CreateToggleButton();
      BringButtonsToFront();
   }

   if(IndicatorHidden)
      return(rates_total);

   if(rates_total < BAR_TO_START_SCAN_FROM + 3)
      return(0);

//--- put start() here
   RefreshObjects();

   return(rates_total);
}

//+------------------------------------------------------------------+
//| Draw / update every fractal level                                |
//+------------------------------------------------------------------+
void RefreshObjects()
{
   int chart_timeframe = (int)Period();

   for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes); timeframe_index++)
   {
      if(chart_timeframe <= timeframes[timeframe_index])
      {
         DrawResistance(GetUpperFractalBar(timeframes[timeframe_index], BAR_TO_START_SCAN_FROM), timeframe_index);
         DrawSupport(GetLowerFractalBar(timeframes[timeframe_index], BAR_TO_START_SCAN_FROM), timeframe_index);
      }
   }
}

//+------------------------------------------------------------------+
void DrawResistance(int bar_index, int timeframe_index)
{
   if(bar_index > 0)
      DrawTrendLine(GetTrendLineName(RESISTANCE, timeframes[timeframe_index]),
                    iHigh(_Symbol, timeframes[timeframe_index], bar_index),
                    colors[timeframe_index], styles[timeframe_index], widths[timeframe_index]);
}

//+------------------------------------------------------------------+
int GetUpperFractalBar(ENUM_TIMEFRAMES timeframe, int starting_bar)
{
   int total = iBars(_Symbol, timeframe);
   if(total <= 0)
      return(-1);

   for(int bar = starting_bar; bar < total - 2; bar++)
      if(IsUpperFractal(timeframe, bar))
         return(bar);

   return(-1);
}

//+------------------------------------------------------------------+
bool IsUpperFractal(ENUM_TIMEFRAMES timeframe, int bar)
{
   double price = iHigh(_Symbol, timeframe, bar);
   if(price == 0)
      return(false);

   for(int offset = -2; offset <= 2; offset++)
   {
      if(offset == 0)
         continue;

      double other = iHigh(_Symbol, timeframe, bar + offset);
      if(other == 0 || other > price)
         return(false);
   }
   return(true);
}

//+------------------------------------------------------------------+
int GetLowerFractalBar(ENUM_TIMEFRAMES timeframe, int starting_bar)
{
   int total = iBars(_Symbol, timeframe);
   if(total <= 0)
      return(-1);

   for(int bar = starting_bar; bar < total - 2; bar++)
      if(IsLowerFractal(timeframe, bar))
         return(bar);

   return(-1);
}

//+------------------------------------------------------------------+
bool IsLowerFractal(ENUM_TIMEFRAMES timeframe, int bar)
{
   double price = iLow(_Symbol, timeframe, bar);
   if(price == 0)
      return(false);

   for(int offset = -2; offset <= 2; offset++)
   {
      if(offset == 0)
         continue;

      double other = iLow(_Symbol, timeframe, bar + offset);
      if(other == 0 || other < price)
         return(false);
   }
   return(true);
}

//+------------------------------------------------------------------+
void DrawSupport(int bar_index, int timeframe_index)
{
   if(bar_index > 0)
      DrawTrendLine(GetTrendLineName(SUPPORT, timeframes[timeframe_index]),
                    iLow(_Symbol, timeframes[timeframe_index], bar_index),
                    colors[timeframe_index], styles[timeframe_index], widths[timeframe_index]);
}

//+------------------------------------------------------------------+
void DrawTrendLine(string object_name, double price, color line_color, int line_style, int line_width)
{
   datetime anchor_time = iTime(_Symbol, _Period, 0);

   if(ObjectFind(0, object_name) < 0)
      ObjectCreate(0, object_name, OBJ_HLINE, 0, anchor_time, price);
   else
      ObjectMove(0, object_name, 0, anchor_time, price);

   ObjectSetInteger(0, object_name, OBJPROP_COLOR,      line_color);
   ObjectSetInteger(0, object_name, OBJPROP_STYLE,      line_style);
   ObjectSetInteger(0, object_name, OBJPROP_WIDTH,      line_width);
   ObjectSetInteger(0, object_name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, object_name, OBJPROP_HIDDEN,     true);

   string label_name = "banzai" + object_name;

   if(!DisplayTextLabel)
   {
      ObjectDelete(0, label_name);
      return;
   }

   datetime label_time = iTime(_Symbol, _Period, 10);
   if(label_time == 0)
      label_time = anchor_time;
   label_time = label_time + PeriodSeconds(_Period) * ShiftLabel;

   if(ObjectFind(0, label_name) < 0)
      ObjectCreate(0, label_name, OBJ_TEXT, 0, label_time, price);
   else
      ObjectMove(0, label_name, 0, label_time, price);

   ObjectSetString (0, label_name, OBJPROP_TEXT,      object_name);
   ObjectSetString (0, label_name, OBJPROP_FONT,      LabelFont);
   ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE,  LabelFontSize);
   ObjectSetInteger(0, label_name, OBJPROP_COLOR,     line_color);
   ObjectSetInteger(0, label_name, OBJPROP_ANCHOR,    ANCHOR_LEFT);
   ObjectSetInteger(0, label_name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, label_name, OBJPROP_HIDDEN,    true);
}

//+------------------------------------------------------------------+
void DeleteSRObjects()
{
   string object_types[2] = {SUPPORT, RESISTANCE};

   for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes); timeframe_index++)
      for(int object_type_index = 0; object_type_index < ArraySize(object_types); object_type_index++)
      {
         string object_name = GetTrendLineName(object_types[object_type_index], timeframes[timeframe_index]);
         ObjectDelete(0, object_name);
         ObjectDelete(0, "banzai" + object_name);
      }
}

//+------------------------------------------------------------------+
string GetTrendLineName(string object_type, ENUM_TIMEFRAMES timeframe)
{
   return(object_type + " " + GetTimeframeName(timeframe));
}

//+------------------------------------------------------------------+
string GetTimeframeName(ENUM_TIMEFRAMES timeframe)
{
   static ENUM_TIMEFRAMES timeframe_periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1};
   static string          timeframe_names[]   = {"M1", "M5", "M15", "M30", "H1", "H4", "D1", "W1", "MN1"};

   for(int i = 0; i < ArraySize(timeframe_periods); i++)
      if(timeframe == timeframe_periods[i])
         return(timeframe_names[i]);

   return("Unknown");
}
//+------------------------------------------------------------------+
