//+------------------------------------------------------------------+
//|           Weekly + Monthly + Yearly OHL (WOHL / MOHL / YOHL)     |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property strict

#property indicator_plots 1
#property indicator_buffers 1
#property indicator_type1   DRAW_NONE
#property indicator_color1  clrNONE

double dummyBuffer[];

//---------------------------
// Inputs
//---------------------------
input bool   ShowPrevHigh       = true;
input color  PrevHighColor      = clrLimeGreen;
input bool   ShowPrevHighText   = true;
input color  PrevHighTextColor  = clrDarkGreen;
input int    PrevHighWidth      = 5;
input ENUM_LINE_STYLE PrevHighStyle = STYLE_SOLID;

input bool   ShowPrevLow        = true;
input color  PrevLowColor       = clrMagenta;
input bool   ShowPrevLowText    = true;
input color  PrevLowTextColor   = clrMaroon;
input int    PrevLowWidth       = 5;
input ENUM_LINE_STYLE PrevLowStyle = STYLE_SOLID;

input bool   ShowOpen           = true;
input color  OpenColor          = clrPurple;
input bool   ShowOpenText       = true;
input int    OpenWidth          = 1;
input ENUM_LINE_STYLE OpenStyle = STYLE_DASH;

input int    RightBars          = 3;

input int    ButtonX            = 0;
input int    ButtonY            = 100;

//---------------------------
// Object Names
//---------------------------
// Weekly
string OBJ_W_H = "W_High";
string OBJ_W_L = "W_Low";
string OBJ_W_O = "W_Open";

// Monthly
string OBJ_M_H = "M_High";
string OBJ_M_L = "M_Low";
string OBJ_M_O = "M_Open";

// Yearly
string OBJ_Y_H = "Y_High";
string OBJ_Y_L = "Y_Low";
string OBJ_Y_O = "Y_Open";

// Buttons
string OBJ_BTN_W = "BTN_WOHL";
string OBJ_BTN_M = "BTN_MOHL";
string OBJ_BTN_Y = "BTN_YOHL";

// Global variables for state persistence
string GV_WOHL = "GV_WOHL";
string GV_MOHL = "GV_MOHL";
string GV_YOHL = "GV_YOHL";

//---------------------------
bool showWeekLines  = true;
bool showMonthLines = false;
bool showYearLines  = false;

//---------------------------
#define BUTTON_WIDTH   55
#define BUTTON_HEIGHT  28

//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer(0, dummyBuffer, INDICATOR_DATA);

   double v;

   // Weekly default ON
   if(GlobalVariableCheck(GV_WOHL)) v = GlobalVariableGet(GV_WOHL); else v = 1.0;
   showWeekLines = (v != 0.0);

   // Monthly default OFF
   if(GlobalVariableCheck(GV_MOHL)) v = GlobalVariableGet(GV_MOHL); else v = 0.0;
   showMonthLines = (v != 0.0);

   // Yearly default OFF
   if(GlobalVariableCheck(GV_YOHL)) v = GlobalVariableGet(GV_YOHL); else v = 0.0;
   showYearLines = (v != 0.0);

   // Buttons use ButtonY directly
   CreateToggleButton(OBJ_BTN_W, "WOHL", ButtonX, ButtonY, showWeekLines);
   CreateToggleButton(OBJ_BTN_M, "MOHL", ButtonX, ButtonY + BUTTON_HEIGHT, showMonthLines);
   CreateToggleButton(OBJ_BTN_Y, "YOHL", ButtonX, ButtonY + 2*BUTTON_HEIGHT, showYearLines);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   DeleteLineAndText(OBJ_W_H);
   DeleteLineAndText(OBJ_W_L);
   DeleteLineAndText(OBJ_W_O);

   DeleteLineAndText(OBJ_M_H);
   DeleteLineAndText(OBJ_M_L);
   DeleteLineAndText(OBJ_M_O);

   DeleteLineAndText(OBJ_Y_H);
   DeleteLineAndText(OBJ_Y_L);
   DeleteLineAndText(OBJ_Y_O);

   if(reason == REASON_REMOVE)
   {
      ObjectDelete(0, OBJ_BTN_W);
      ObjectDelete(0, OBJ_BTN_M);
      ObjectDelete(0, OBJ_BTN_Y);
   }
}

//+------------------------------------------------------------------+
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[])
{
   // WEEKLY OHL
   datetime prevWeekStart, prevWeekEnd, thisWeekStart;
   GetWeekBoundaries(prevWeekStart, prevWeekEnd, thisWeekStart);

   double wHigh, wLow;
   datetime wHighTime, wLowTime;
   GetPrevWeekHL(prevWeekStart, prevWeekEnd, wHigh, wLow, wHighTime, wLowTime);

   double wOpen;
   datetime wOpenTime;
   GetThisWeekOpen(thisWeekStart, wOpen, wOpenTime);

   DrawLine(OBJ_W_H, wHighTime, wHigh, PrevHighColor, PrevHighWidth, PrevHighStyle,
            showWeekLines && ShowPrevHigh, showWeekLines && ShowPrevHighText, PrevHighTextColor, "Last Week HIGH");

   DrawLine(OBJ_W_L, wLowTime, wLow, PrevLowColor, PrevLowWidth, PrevLowStyle,
            showWeekLines && ShowPrevLow, showWeekLines && ShowPrevLowText, PrevLowTextColor, "Last Week LOW");

   DrawLine(OBJ_W_O, wOpenTime, wOpen, OpenColor, OpenWidth, OpenStyle,
            showWeekLines && ShowOpen, showWeekLines && ShowOpenText, OpenColor, "This Week OPEN");

   // MONTHLY OHL (LAST MONTH)
   datetime prevMonthStart, prevMonthEnd, thisMonthStart;
   GetMonthBoundaries(prevMonthStart, prevMonthEnd, thisMonthStart);

   double mHigh, mLow;
   datetime mHighTime, mLowTime;
   GetPrevMonthHL(prevMonthStart, prevMonthEnd, mHigh, mLow, mHighTime, mLowTime);

   double mOpen;
   datetime mOpenTime;
   GetThisMonthOpen(thisMonthStart, mOpen, mOpenTime);

   DrawLine(OBJ_M_H, mHighTime, mHigh, PrevHighColor, PrevHighWidth, PrevHighStyle,
            showMonthLines && ShowPrevHigh, showMonthLines && ShowPrevHighText, PrevHighTextColor, "Last Month HIGH");

   DrawLine(OBJ_M_L, mLowTime, mLow, PrevLowColor, PrevLowWidth, PrevLowStyle,
            showMonthLines && ShowPrevLow, showMonthLines && ShowPrevLowText, PrevLowTextColor, "Last Month LOW");

   DrawLine(OBJ_M_O, mOpenTime, mOpen, OpenColor, OpenWidth, OpenStyle,
            showMonthLines && ShowOpen, showMonthLines && ShowOpenText, OpenColor, "This Month OPEN");

   // YEARLY OHL (LAST YEAR)
   datetime prevYearStart, prevYearEnd, thisYearStart;
   GetYearBoundaries(prevYearStart, prevYearEnd, thisYearStart);

   double yHigh, yLow;
   datetime yHighTime, yLowTime;
   GetPrevYearHL(prevYearStart, prevYearEnd, yHigh, yLow, yHighTime, yLowTime);

   double yOpen;
   datetime yOpenTime;
   GetThisYearOpen(thisYearStart, yOpen, yOpenTime);

   DrawLine(OBJ_Y_H, yHighTime, yHigh, PrevHighColor, PrevHighWidth, PrevHighStyle,
            showYearLines && ShowPrevHigh, showYearLines && ShowPrevHighText, PrevHighTextColor, "Last Year HIGH");

   DrawLine(OBJ_Y_L, yLowTime, yLow, PrevLowColor, PrevLowWidth, PrevLowStyle,
            showYearLines && ShowPrevLow, showYearLines && ShowPrevLowText, PrevLowTextColor, "Last Year LOW");

   DrawLine(OBJ_Y_O, yOpenTime, yOpen, OpenColor, OpenWidth, OpenStyle,
            showYearLines && ShowOpen, showYearLines && ShowOpenText, OpenColor, "This Year OPEN");

   return(rates_total);
}

//+------------------------------------------------------------------+
void DeleteLineAndText(string base)
{
   ObjectDelete(0, base);
   ObjectDelete(0, base + "_txt");
}

//+------------------------------------------------------------------+
// WEEK BOUNDARIES
//+------------------------------------------------------------------+
void GetWeekBoundaries(datetime &prevStart, datetime &prevEnd, datetime &thisStart)
{
   MqlDateTime t;
   TimeToStruct(TimeCurrent(), t);

   int dow = t.day_of_week;
   if(dow == 0) dow = 7;

   thisStart = StructToTime(t) - (dow - 1) * 86400;
   thisStart = NormalizeDayStart(thisStart);

   prevStart = thisStart - 7 * 86400;
   prevEnd   = thisStart - 1;
}

datetime NormalizeDayStart(datetime dt)
{
   MqlDateTime s;
   TimeToStruct(dt, s);
   s.hour = 0; s.min = 0; s.sec = 0;
   return StructToTime(s);
}

//+------------------------------------------------------------------+
// PREVIOUS WEEK HIGH/LOW
//+------------------------------------------------------------------+
void GetPrevWeekHL(datetime ws, datetime we,
                   double &ph, double &pl,
                   datetime &phTime, datetime &plTime)
{
   ph = -DBL_MAX;
   pl =  DBL_MAX;

   int start = iBarShift(NULL, PERIOD_M15, ws, false);
   int end   = iBarShift(NULL, PERIOD_M15, we, false);

   for(int i = end; i <= start; i++)
   {
      double h = iHigh(NULL, PERIOD_M15, i);
      double l = iLow(NULL, PERIOD_M15, i);
      datetime t = iTime(NULL, PERIOD_M15, i);

      if(h > ph) { ph = h; phTime = t; }
      if(l < pl) { pl = l; plTime = t; }
   }
}

//+------------------------------------------------------------------+
// THIS WEEK OPEN
//+------------------------------------------------------------------+
void GetThisWeekOpen(datetime ws, double &wo, datetime &woTime)
{
   int shift = iBarShift(NULL, PERIOD_M15, ws, false);
   wo     = iOpen(NULL, PERIOD_M15, shift);
   woTime = iTime(NULL, PERIOD_M15, shift);
}

//+------------------------------------------------------------------+
// MONTH BOUNDARIES (LAST MONTH + THIS MONTH)
//+------------------------------------------------------------------+
void GetMonthBoundaries(datetime &prevMonthStart, datetime &prevMonthEnd, datetime &thisMonthStart)
{
   MqlDateTime t;
   TimeToStruct(TimeCurrent(), t);

   // THIS MONTH START
   t.day = 1;
   t.hour = 0;
   t.min = 0;
   t.sec = 0;
   thisMonthStart = StructToTime(t);

   // LAST MONTH START
   t.mon--;
   if(t.mon == 0) { t.mon = 12; t.year--; }
   prevMonthStart = StructToTime(t);

   // LAST MONTH END
   t.mon++;
   if(t.mon == 13) { t.mon = 1; t.year++; }
   t.day = 1;
   datetime firstThisMonth = StructToTime(t);
   prevMonthEnd = firstThisMonth - 1;
}

//+------------------------------------------------------------------+
// PREVIOUS MONTH HIGH/LOW
//+------------------------------------------------------------------+
void GetPrevMonthHL(datetime ms, datetime me,
                    double &ph, double &pl,
                    datetime &phTime, datetime &plTime)
{
   ph = -DBL_MAX;
   pl =  DBL_MAX;

   int start = iBarShift(NULL, PERIOD_M15, ms, false);
   int end   = iBarShift(NULL, PERIOD_M15, me, false);

   for(int i = end; i <= start; i++)
   {
      double h = iHigh(NULL, PERIOD_M15, i);
      double l = iLow(NULL, PERIOD_M15, i);
      datetime t = iTime(NULL, PERIOD_M15, i);

      if(h > ph) { ph = h; phTime = t; }
      if(l < pl) { pl = l; plTime = t; }
   }
}

//+------------------------------------------------------------------+
// THIS MONTH OPEN
//+------------------------------------------------------------------+
void GetThisMonthOpen(datetime ms, double &mo, datetime &moTime)
{
   int shift = iBarShift(NULL, PERIOD_M15, ms, false);
   mo     = iOpen(NULL, PERIOD_M15, shift);
   moTime = iTime(NULL, PERIOD_M15, shift);
}

//+------------------------------------------------------------------+
// YEAR BOUNDARIES (LAST YEAR + THIS YEAR)
//+------------------------------------------------------------------+
void GetYearBoundaries(datetime &prevYearStart, datetime &prevYearEnd, datetime &thisYearStart)
{
   MqlDateTime t;
   TimeToStruct(TimeCurrent(), t);

   // THIS YEAR START
   t.mon = 1;
   t.day = 1;
   t.hour = 0;
   t.min = 0;
   t.sec = 0;
   thisYearStart = StructToTime(t);

   // LAST YEAR START
   t.year--;
   prevYearStart = StructToTime(t);

   // LAST YEAR END
   t.mon = 12;
   t.day = 31;
   t.hour = 23;
   t.min = 59;
   t.sec = 59;
   prevYearEnd = StructToTime(t);
}

//+------------------------------------------------------------------+
// PREVIOUS YEAR HIGH/LOW
//+------------------------------------------------------------------+
void GetPrevYearHL(datetime ys, datetime ye,
                   double &ph, double &pl,
                   datetime &phTime, datetime &plTime)
{
   ph = -DBL_MAX;
   pl =  DBL_MAX;

   int start = iBarShift(NULL, PERIOD_M15, ys, false);
   int end   = iBarShift(NULL, PERIOD_M15, ye, false);

   for(int i = end; i <= start; i++)
   {
      double h = iHigh(NULL, PERIOD_M15, i);
      double l = iLow(NULL, PERIOD_M15, i);
      datetime t = iTime(NULL, PERIOD_M15, i);

      if(h > ph) { ph = h; phTime = t; }
      if(l < pl) { pl = l; plTime = t; }
   }
}

//+------------------------------------------------------------------+
// THIS YEAR OPEN
//+------------------------------------------------------------------+
void GetThisYearOpen(datetime ys, double &yo, datetime &yoTime)
{
   int shift = iBarShift(NULL, PERIOD_M15, ys, false);
   yo     = iOpen(NULL, PERIOD_M15, shift);
   yoTime = iTime(NULL, PERIOD_M15, shift);
}

//+------------------------------------------------------------------+
// DRAW LINE + TEXT
//+------------------------------------------------------------------+
void DrawLine(string name, datetime t0, double price,
              color clr, int width, ENUM_LINE_STYLE style,
              bool show, bool showText, color textClr,
              string label)
{
   if(!show)
   {
      ObjectDelete(0, name);
      ObjectDelete(0, name + "_txt");
      return;
   }

   datetime t1 = TimeCurrent() + (RightBars * PeriodSeconds(PERIOD_CURRENT));

   ObjectCreate(0, name, OBJ_TREND, 0, t0, price, t1, price);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);

   if(showText)
   {
      string txt = name + "_txt";
      datetime tText = t1 + PeriodSeconds(PERIOD_CURRENT);

      ObjectCreate(0, txt, OBJ_TEXT, 0, tText, price);
      ObjectSetString(0, txt, OBJPROP_TEXT, label);

      ObjectSetInteger(0, txt, OBJPROP_COLOR, textClr);
      ObjectSetInteger(0, txt, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, txt, OBJPROP_ANCHOR, ANCHOR_LEFT);
   }
}

//+------------------------------------------------------------------+
// BUTTON ENGINE
//+------------------------------------------------------------------+
void CreateToggleButton(const string name,
                        const 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);

   color bg = state ? clrLightGreen : clrLightPink;
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);

   ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, 0);
   ObjectSetInteger(0, name, OBJPROP_YOFFSET, -2);
}

//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if(id != CHARTEVENT_OBJECT_CLICK)
      return;

   // WEEK toggle
   if(sparam == OBJ_BTN_W)
   {
      showWeekLines = !showWeekLines;
      GlobalVariableSet(GV_WOHL, showWeekLines ? 1.0 : 0.0);

      CreateToggleButton(OBJ_BTN_W, "WOHL",
                         ButtonX,
                         ButtonY,
                         showWeekLines);
   }

   // MONTH toggle
   else if(sparam == OBJ_BTN_M)
   {
      showMonthLines = !showMonthLines;
      GlobalVariableSet(GV_MOHL, showMonthLines ? 1.0 : 0.0);

      CreateToggleButton(OBJ_BTN_M, "MOHL",
                         ButtonX,
                         ButtonY + BUTTON_HEIGHT,
                         showMonthLines);
   }

   // YEAR toggle
   else if(sparam == OBJ_BTN_Y)
   {
      showYearLines = !showYearLines;
      GlobalVariableSet(GV_YOHL, showYearLines ? 1.0 : 0.0);

      CreateToggleButton(OBJ_BTN_Y, "YOHL",
                         ButtonX,
                         ButtonY + 2*BUTTON_HEIGHT,
                         showYearLines);
   }

   ChartRedraw();
}
//+------------------------------------------------------------------+
