//+------------------------------------------------------------------+
//|                                     Daily Line Boxes Buttons.mq5 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026"
#property link      "https://www.mql5.com"
#property indicator_chart_window
#property strict
#property indicator_plots   1
#property indicator_buffers 1
#property indicator_type1   DRAW_NONE
#property indicator_color1  clrNONE
#property version   "2.101"

// Dummy buffer (indicator draws objects only)
double dummyBuffer[];

//------------------------------------------------------------------
// BASIC INPUTS
//------------------------------------------------------------------
input int      LookbackDays        = 20;
input int      StartHour           = 0;
input int      EndHour             = 24;

input bool     ShowBox             = true;
input bool     ShowHighLine        = true;
input bool     ShowLowLine         = true;
input bool     ShowMidpoint        = true;

input bool     ShowRangeLabel      = true;
input int      ClearancePercent    = 25;

input color    BoxColor            = clrLightCyan;
input color    HighLineColor       = clrLime;
input color    LowLineColor        = clrRed;
input color    MidpointColor       = clrDodgerBlue;
input color    RangeLabelColor     = clrDarkBlue;

//------------------------------------------------------------------
// GLOBAL EXTENSION SETTINGS
//------------------------------------------------------------------
input int      ExtendHighHours     = 48;
input int      ExtendLowHours      = 48;
input int      ExtendMidHours      = 48;

//------------------------------------------------------------------
// PER-DAY EXTENSIONS
//------------------------------------------------------------------
input bool     UsePerDayExtensions = true;

input int MonHigh = 48;
input int MonLow  = 48;
input int MonMid  = 48;

input int TueHigh = 48;
input int TueLow  = 48;
input int TueMid  = 48;

input int WedHigh = 48;
input int WedLow  = 48;
input int WedMid  = 48;

input int ThuHigh = 48;
input int ThuLow  = 48;
input int ThuMid  = 48;

input int FriHigh = 96;
input int FriLow  = 96;
input int FriMid  = 96;

//------------------------------------------------------------------
// BUTTON UI — USER CAN MOVE, BUT NOT RESIZE
//------------------------------------------------------------------
input int ButtonX = 10;
input int ButtonY = 200;

// Fixed internal button size
#define BUTTON_WIDTH   45
#define BUTTON_HEIGHT  28

//------------------------------------------------------------------
// INTERNAL GLOBALS
//------------------------------------------------------------------
double g_point;
int    g_digits;

string prefix = "DLB_";

string GV_MON = "DLB_MON";
string GV_TUE = "DLB_TUE";
string GV_WED = "DLB_WED";
string GV_THU = "DLB_THU";
string GV_FRI = "DLB_FRI";

bool ShowMon = true;
bool ShowTue = true;
bool ShowWed = true;
bool ShowThu = true;
bool ShowFri = true;

//------------------------------------------------------------------
// PART 2 — CORE HELPERS
//------------------------------------------------------------------
void DeleteAllWithPrefix(const string p)
{
   int total = ObjectsTotal(0, -1, -1);

   for (int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i);

      if (StringFind(name, p, 0) == 0)
      {
         if (StringFind(name, "BTN_") == 0)
            continue;

         ObjectDelete(0, name);
      }
   }
}

void CreateDayButton(string name, string label, int x, int y, bool state)
{
   if (ObjectFind(0, name) == -1)
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);

   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);

   ObjectSetInteger(0, name, OBJPROP_XSIZE, BUTTON_WIDTH);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, BUTTON_HEIGHT);

   ObjectSetString(0, name, OBJPROP_TEXT, label);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 12);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clrBlack);

   // ON = green, OFF = pink
   color bg = state ? clrLightGreen : clrLightPink;
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);

   // Remove borders completely
   ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, 0);

   // Slight upward text shift for visual centering
   ObjectSetInteger(0, name, OBJPROP_YOFFSET, -2);
}

bool DayIsEnabled(int dow)
{
   if (dow == 1 && !ShowMon) return(false);
   if (dow == 2 && !ShowTue) return(false);
   if (dow == 3 && !ShowWed) return(false);
   if (dow == 4 && !ShowThu) return(false);
   if (dow == 5 && !ShowFri) return(false);

   return(true);
}

void GetExtensionsForDay(int dow, int &extH, int &extL, int &extM)
{
   if (UsePerDayExtensions)
   {
      if (dow == 1) { extH = MonHigh; extL = MonLow; extM = MonMid; return; }
      if (dow == 2) { extH = TueHigh; extL = TueLow; extM = TueMid; return; }
      if (dow == 3) { extH = WedHigh; extL = WedLow; extM = WedMid; return; }
      if (dow == 4) { extH = ThuHigh; extL = ThuLow; extM = ThuMid; return; }
      if (dow == 5) { extH = FriHigh; extL = FriLow; extM = FriMid; return; }
   }

   extH = ExtendHighHours;
   extL = ExtendLowHours;
   extM = ExtendMidHours;
}

//------------------------------------------------------------------
// PART 3 — DRAWING HELPERS
//------------------------------------------------------------------
void DrawBox(const string tag, datetime dtStart, datetime dtEnd, double hi, double lo)
{
   if (!ShowBox) return;

   string name = tag + "_BOX";
   ObjectCreate(0, name, OBJ_RECTANGLE, 0, dtStart, hi, dtEnd, lo);
   ObjectSetInteger(0, name, OBJPROP_COLOR, BoxColor);
   ObjectSetInteger(0, name, OBJPROP_FILL, true);
}

void DrawRealHigh(const string tag, datetime dtStart, datetime dtHighEnd, double hi)
{
   if (!ShowHighLine) return;

   string name = tag + "_REAL_HI";
   ObjectCreate(0, name, OBJ_TREND, 0, dtStart, hi, dtHighEnd, hi);
   ObjectSetInteger(0, name, OBJPROP_COLOR, HighLineColor);
}

void DrawRealLow(const string tag, datetime dtStart, datetime dtLowEnd, double lo)
{
   if (!ShowLowLine) return;

   string name = tag + "_REAL_LO";
   ObjectCreate(0, name, OBJ_TREND, 0, dtStart, lo, dtLowEnd, lo);
   ObjectSetInteger(0, name, OBJPROP_COLOR, LowLineColor);
}

void DrawExpandedHigh(const string tag, datetime dtEnd, datetime dtHighEnd, double hi_expanded)
{
   if (!ShowHighLine) return;

   string name = tag + "_HIGHER";
   ObjectCreate(0, name, OBJ_TREND, 0, dtEnd, hi_expanded, dtHighEnd, hi_expanded);
   ObjectSetInteger(0, name, OBJPROP_COLOR, HighLineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
}

void DrawExpandedLow(const string tag, datetime dtEnd, datetime dtLowEnd, double lo_expanded)
{
   if (!ShowLowLine) return;

   string name = tag + "_LOWER";
   ObjectCreate(0, name, OBJ_TREND, 0, dtEnd, lo_expanded, dtLowEnd, lo_expanded);
   ObjectSetInteger(0, name, OBJPROP_COLOR, LowLineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
}

void DrawMidpointLine(const string tag, datetime dtEnd, datetime dtMidEnd, double mid)
{
   if (!ShowMidpoint) return;

   string name = tag + "_MID";
   ObjectCreate(0, name, OBJ_TREND, 0, dtEnd, mid, dtMidEnd, mid);
   ObjectSetInteger(0, name, OBJPROP_COLOR, MidpointColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DASH);
}

void DrawRangeLabel(const string tag, datetime dtStart, double hi, double range)
{
   if (!ShowRangeLabel) return;

   int pips = (int)MathRound(range / g_point);
   string name = tag + "_RANGE";

   datetime dtLabel = dtStart + PeriodSeconds(_Period);
   double   yLabel  = hi + (range * 0.10);

   ObjectCreate(0, name, OBJ_TEXT, 0, dtLabel, yLabel);
   ObjectSetString(0, name, OBJPROP_TEXT, IntegerToString(pips));
   ObjectSetInteger(0, name, OBJPROP_COLOR, RangeLabelColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 12);
   ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT);
}

//------------------------------------------------------------------
// PART 4 — CORE DAY ENGINE
//------------------------------------------------------------------
bool DrawDayByIndex(int index)
{
   datetime now = TimeCurrent();
   datetime dayTime = now - index * 86400;

   MqlDateTime dt;
   TimeToStruct(dayTime, dt);
   dt.hour = dt.min = dt.sec = 0;
   datetime base = StructToTime(dt);

   MqlDateTime t2;
   TimeToStruct(base, t2);
   int dow = t2.day_of_week;

   if (dow == 0 || dow == 6)
      return(false);
   if (!DayIsEnabled(dow))
      return(false);

   datetime dtStart = base + StartHour * 3600 + 60;
   datetime dtEnd   = base + EndHour   * 3600;
   if (dtEnd <= dtStart)
      dtEnd = dtStart + 3600;

   int barStart = iBarShift(_Symbol, _Period, dtStart, false);
   int barEnd   = iBarShift(_Symbol, _Period, dtEnd,   false);

   if (barStart < 0 || barEnd < 0)
      return(false);
   if (barStart == barEnd)
      return(false);

   double hi = -DBL_MAX;
   double lo =  DBL_MAX;

   for (int j = barStart; j > barEnd; j--)
   {
      double h = iHigh(_Symbol, _Period, j);
      double l = iLow (_Symbol, _Period, j);

      if (h > hi) hi = h;
      if (l < lo) lo = l;
   }

   if (hi == -DBL_MAX || lo == DBL_MAX)
      return(false);

   double range  = hi - lo;
   double buffer = range * (ClearancePercent / 100.0);

   double hi_expanded = hi + buffer;
   double lo_expanded = lo - buffer;
   double mid         = (hi + lo) / 2.0;

   int extH, extL, extM;
   GetExtensionsForDay(dow, extH, extL, extM);

   datetime dtHighEnd = base + extH * 3600;
   datetime dtLowEnd  = base + extL * 3600;
   datetime dtMidEnd  = base + extM * 3600;

   string tag = prefix + IntegerToString(index);

   DrawBox(tag, dtStart, dtEnd, hi, lo);
   DrawRealHigh(tag, dtStart, dtHighEnd, hi);
   DrawRealLow(tag, dtStart, dtLowEnd, lo);
   DrawExpandedHigh(tag, dtEnd, dtHighEnd, hi_expanded);
   DrawExpandedLow(tag, dtEnd, dtLowEnd, lo_expanded);
   DrawMidpointLine(tag, dtEnd, dtMidEnd, mid);
   DrawRangeLabel(tag, dtStart, hi, range);

   return(true);
}

void DrawDailyBoxes()
{
   DeleteAllWithPrefix(prefix);

   g_point  = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   g_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   if (g_digits == 3 || g_digits == 5)
      g_point *= 10.0;

   for (int i = 0; i < LookbackDays; i++)
      DrawDayByIndex(i);
}

void DrawSingleDay(int dayIndex)
{
   string tag = prefix + IntegerToString(dayIndex);

   int total = ObjectsTotal(0, -1, -1);
   for (int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i);
      if (StringFind(name, tag, 0) == 0)
         ObjectDelete(0, name);
   }

   DrawDayByIndex(dayIndex);
}

//------------------------------------------------------------------
// PART 5 — INIT / DEINIT / CALCULATE
//------------------------------------------------------------------
int OnInit()
{
   SetIndexBuffer(0, dummyBuffer, INDICATOR_DATA);

   double v;

   if (GlobalVariableCheck(GV_MON)) v = GlobalVariableGet(GV_MON); else v = 1.0;
   ShowMon = (v != 0.0);

   if (GlobalVariableCheck(GV_TUE)) v = GlobalVariableGet(GV_TUE); else v = 1.0;
   ShowTue = (v != 0.0);

   if (GlobalVariableCheck(GV_WED)) v = GlobalVariableGet(GV_WED); else v = 1.0;
   ShowWed = (v != 0.0);

   if (GlobalVariableCheck(GV_THU)) v = GlobalVariableGet(GV_THU); else v = 1.0;
   ShowThu = (v != 0.0);

   if (GlobalVariableCheck(GV_FRI)) v = GlobalVariableGet(GV_FRI); else v = 1.0;
   ShowFri = (v != 0.0);

   if (!ShowMon && !ShowTue && !ShowWed && !ShowThu && !ShowFri)
   {
      ShowMon = ShowTue = ShowWed = ShowThu = ShowFri = true;

      GlobalVariableSet(GV_MON, 1.0);
      GlobalVariableSet(GV_TUE, 1.0);
      GlobalVariableSet(GV_WED, 1.0);
      GlobalVariableSet(GV_THU, 1.0);
      GlobalVariableSet(GV_FRI, 1.0);
   }

   DeleteAllWithPrefix(prefix);

   int y = ButtonY;

   CreateDayButton("BTN_MON", "MON", ButtonX, y, ShowMon);
   y += BUTTON_HEIGHT;

   CreateDayButton("BTN_TUE", "TUE", ButtonX, y, ShowTue);
   y += BUTTON_HEIGHT;

   CreateDayButton("BTN_WED", "WED", ButtonX, y, ShowWed);
   y += BUTTON_HEIGHT;

   CreateDayButton("BTN_THU", "THU", ButtonX, y, ShowThu);
   y += BUTTON_HEIGHT;

   CreateDayButton("BTN_FRI", "FRI", ButtonX, y, ShowFri);

   DrawDailyBoxes();
   ChartRedraw();

   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   DeleteAllWithPrefix(prefix);

   ObjectDelete(0, "BTN_MON");
   ObjectDelete(0, "BTN_TUE");
   ObjectDelete(0, "BTN_WED");
   ObjectDelete(0, "BTN_THU");
   ObjectDelete(0, "BTN_FRI");
}

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)
   {
      DrawDailyBoxes();
      return(rates_total);
   }

   if (time[0] != time[1])
      DrawDailyBoxes();

   return(rates_total);
}

//------------------------------------------------------------------
// PART 6 — ONCHARTEVENT
//------------------------------------------------------------------
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if (id != CHARTEVENT_OBJECT_CLICK)
      return;

   int y0 = ButtonY;

   if (sparam == "BTN_MON")
   {
      ShowMon = !ShowMon;
      GlobalVariableSet(GV_MON, ShowMon ? 1.0 : 0.0);
      CreateDayButton("BTN_MON", "MON", ButtonX, y0 + 0*(BUTTON_HEIGHT), ShowMon);
      DrawSingleDay(0);
   }
   else if (sparam == "BTN_TUE")
   {
      ShowTue = !ShowTue;
      GlobalVariableSet(GV_TUE, ShowTue ? 1.0 : 0.0);
      CreateDayButton("BTN_TUE", "TUE", ButtonX, y0 + 1*(BUTTON_HEIGHT), ShowTue);
      DrawSingleDay(1);
   }
   else if (sparam == "BTN_WED")
   {
      ShowWed = !ShowWed;
      GlobalVariableSet(GV_WED, ShowWed ? 1.0 : 0.0);
      CreateDayButton("BTN_WED", "WED", ButtonX, y0 + 2*(BUTTON_HEIGHT), ShowWed);
      DrawSingleDay(2);
   }
   else if (sparam == "BTN_THU")
   {
      ShowThu = !ShowThu;
      GlobalVariableSet(GV_THU, ShowThu ? 1.0 : 0.0);
      CreateDayButton("BTN_THU", "THU", ButtonX, y0 + 3*(BUTTON_HEIGHT), ShowThu);
      DrawSingleDay(3);
   }
   else if (sparam == "BTN_FRI")
   {
      ShowFri = !ShowFri;
      GlobalVariableSet(GV_FRI, ShowFri ? 1.0 : 0.0);
      CreateDayButton("BTN_FRI", "FRI", ButtonX, y0 + 4*(BUTTON_HEIGHT), ShowFri);
      DrawSingleDay(4);
   }

   // ------------------------------------------------------------
   // SAFETY RESET — if all days disabled, re-enable all
   // ------------------------------------------------------------
   if (!ShowMon && !ShowTue && !ShowWed && !ShowThu && !ShowFri)
   {
      ShowMon = ShowTue = ShowWed = ShowThu = ShowFri = true;

      GlobalVariableSet(GV_MON, 1.0);
      GlobalVariableSet(GV_TUE, 1.0);
      GlobalVariableSet(GV_WED, 1.0);
      GlobalVariableSet(GV_THU, 1.0);
      GlobalVariableSet(GV_FRI, 1.0);

      int y = ButtonY;

      CreateDayButton("BTN_MON", "MON", ButtonX, y, ShowMon);
      y += BUTTON_HEIGHT;

      CreateDayButton("BTN_TUE", "TUE", ButtonX, y, ShowTue);
      y += BUTTON_HEIGHT;

      CreateDayButton("BTN_WED", "WED", ButtonX, y, ShowWed);
      y += BUTTON_HEIGHT;

      CreateDayButton("BTN_THU", "THU", ButtonX, y, ShowThu);
      y += BUTTON_HEIGHT;

      CreateDayButton("BTN_FRI", "FRI", ButtonX, y, ShowFri);

      DrawDailyBoxes();
      ChartRedraw();
   }
}
