//+------------------------------------------------------------------+
//|                                              MarketStructure.mq4 |
//|                     Direct MQL4 Port of MT5 Larry Williams Logic |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Chacha Ian Maroa"
#property link      "https://www.mql5.com/en/users/chachaian"
#property strict

#property indicator_chart_window
#property indicator_buffers 6

#property indicator_color1 clrSpringGreen 
#property indicator_color2 clrHotPink     
#property indicator_color3 clrSpringGreen 
#property indicator_color4 clrHotPink     
#property indicator_color5 clrSpringGreen 
#property indicator_color6 clrHotPink     

#property indicator_width3 2
#property indicator_width4 2
#property indicator_width5 5
#property indicator_width6 5

//--- Inputs
extern int  ShortTermGapPts = 10;            
extern int  IntermediateTermGapPts = 20;    
extern int  LongTermGapPts = 20;            

extern bool DisplayShortTermArrows = false;
extern bool DisplayIntermediateTermArrows = true;
extern bool DisplayLongTermArrows = true;

extern int  ShortTermUpArrowCode = 161;
extern int  ShortTermDownArrowCode = 161;
extern int  IntermediateTermUpArrowCode = 161;
extern int  IntermediateTermDownArrowCode = 161;
extern int  LongTermUpArrowCode = 221;
extern int  LongTermDownArrowCode = 222;

//--- Chart Appearance Inputs
extern bool EnableTrendCandleColoring    = false; 
extern color new_CHART_COLOR_BACKGROUND  = clrWhite;
extern color new_CHART_COLOR_FOREGROUND  = clrBlack;
extern bool  new_CHART_SHOW_GRID         = false;
extern color new_CHART_COLOR_CANDLE_BULL = clrSeaGreen;
extern color new_CHART_COLOR_CANDLE_BEAR = clrBlack;
extern color new_CHART_COLOR_CHART_UP    = clrSeaGreen;
extern color new_CHART_COLOR_CHART_DOWN  = clrBlack;

//Forex-Station button template start41; copy and paste
extern string             button_note1          = "------------------------------"; // ------------------------------
extern int                btn_Subwindow         = 0;                                // What window to put the button on.  If <0, the button will use the same sub-window as the indicator.
extern ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER;                // button corner on chart for anchoring
extern string             btn_text              = "MARKET";                         // a button name
extern string             btn_Font              = "Arial";                          // button font name
extern int                btn_FontSize          = 9;                                // button font size               
extern color              btn_text_ON_color     = clrLime;                          // ON color when the button is turned on
extern color              btn_text_OFF_color    = clrRed;                           // OFF color when the button is turned off
extern color              btn_background_color  = clrDimGray;                       // background color of the button
extern color              btn_border_color      = clrBlack;                         // border color the button
extern int                button_x              = 20;                               // x coordinate of the button     
extern int                button_y              = 155;                              // y coordinate of the button     
extern int                btn_Width             = 80;                               // button width
extern int                btn_Height            = 20;                               // button height
extern string             UniqueButtonID        = "structure";                      // Unique ID for each button        
extern string             button_note2          = "------------------------------"; // ------------------------------
bool show_data, recalc=false;
string IndicatorObjPrefix, buttonId;
//"",btn_Subwindow,btn_corner,btn_text,btn_Font,btn_FontSize,btn_text_ON_color,btn_text_OFF_color,btn_background_color,btn_border_color,button_x,button_y,btn_Width,btn_Height,UniqueButtonID,"",
//Forex-Station button template end41; copy and paste

//--- Buffers
double shortTermLows[], shortTermHighs[], intermediateTermLows[], intermediateTermHighs[], longTermLows[], longTermHighs[];
int currentTrend = 0; 
//--- Global Variables to store original user colors
long old_bg, old_fg, old_grid, old_up, old_down, old_bull, old_bear;

//+------------------------------------------------------------------------------------------------------------------+
//Forex-Station button template start42; copy and paste
int OnInit()
{
   IndicatorDigits(Digits);  
   IndicatorObjPrefix = "__" + btn_text + "__";
   
   // The leading "_" gives buttonId a *unique* prefix.  Furthermore, prepending the swin is usually unique unless >2+ of THIS indy are displayed in the SAME sub-window. (But, if >2 used, be sure to shift the buttonId position)
   buttonId = "_" + UniqueButtonID + IndicatorObjPrefix + "_BT_";
   if (ObjectFind(buttonId)<0) 
      createButton(buttonId, btn_text, btn_Width, btn_Height, btn_Font, btn_FontSize, btn_background_color, btn_border_color, btn_text_ON_color);
   ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE, button_y);
   ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE, button_x);

   init2();
   //--- Save user's current colors
   old_bg   = ChartGetInteger(0, CHART_COLOR_BACKGROUND);
   old_fg   = ChartGetInteger(0, CHART_COLOR_FOREGROUND);
   old_grid = ChartGetInteger(0, CHART_COLOR_GRID);
   old_up   = ChartGetInteger(0, CHART_COLOR_CHART_UP);
   old_down = ChartGetInteger(0, CHART_COLOR_CHART_DOWN);
   old_bull = ChartGetInteger(0, CHART_COLOR_CANDLE_BULL);
   old_bear = ChartGetInteger(0, CHART_COLOR_CANDLE_BEAR);
    
   if (GetButtonState(buttonId)!="off")
       {
        ConfigureChartAppearance();
       }
   else 
       {
         for (int Forex=0; Forex<indicator_buffers; Forex++) SetIndexStyle(Forex,DRAW_NONE);
         restoreOriginalColors();
       }
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------------------------------------------------------+
void createButton(string buttonID,string buttonText,int width2,int height,string font,int fontSize,color bgColor,color borderColor,color txtColor)
{
      ObjectDelete    (0,buttonID);
      ObjectCreate    (0,buttonID,OBJ_BUTTON,btn_Subwindow,0,0);
      ObjectSetInteger(0,buttonID,OBJPROP_COLOR,txtColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BGCOLOR,bgColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BORDER_COLOR,borderColor);
      ObjectSetInteger(0,buttonID,OBJPROP_BORDER_TYPE,BORDER_RAISED);
      ObjectSetInteger(0,buttonID,OBJPROP_XSIZE,width2);
      ObjectSetInteger(0,buttonID,OBJPROP_YSIZE,height);
      ObjectSetString (0,buttonID,OBJPROP_FONT,font);
      ObjectSetString (0,buttonID,OBJPROP_TEXT,buttonText);
      ObjectSetInteger(0,buttonID,OBJPROP_FONTSIZE,fontSize);
      ObjectSetInteger(0,buttonID,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(0,buttonID,OBJPROP_CORNER,btn_corner);
      ObjectSetInteger(0,buttonID,OBJPROP_HIDDEN,true);
      ObjectSetInteger(0,buttonID,OBJPROP_XDISTANCE,9999);
      ObjectSetInteger(0,buttonID,OBJPROP_YDISTANCE,9999);
      // Upon creation, set the initial state to "true" which is "on", so one will see the indicator by default
      ObjectSetInteger(0, buttonId, OBJPROP_STATE, true);
}
//+------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{ 
      switch(reason)
      {
         case REASON_PARAMETERS  :
         case REASON_CHARTCHANGE :
         case REASON_RECOMPILE   :
         case REASON_CLOSE       : break;
         default                 : ObjectDelete(buttonId);
      }

      // Restore original colors captured in OnInit
      ChartSetInteger(0, CHART_COLOR_BACKGROUND,  (color)old_bg);
      ChartSetInteger(0, CHART_COLOR_FOREGROUND,  (color)old_fg);
      ChartSetInteger(0, CHART_COLOR_GRID,        (color)old_grid);
      ChartSetInteger(0, CHART_COLOR_CHART_UP,    (color)old_up);
      ChartSetInteger(0, CHART_COLOR_CHART_DOWN,  (color)old_down);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, (color)old_bull);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, (color)old_bear);
      deinit2();
      
      ChartRedraw();
}
//+------------------------------------------------------------------------------------------------------------------+
string GetButtonState(string whichbutton)
{
      bool selected = ObjectGetInteger(ChartID(),whichbutton,OBJPROP_STATE);
      if (selected)
           { return ("on"); } 
      else { return ("off");}
}
//+------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
  // If another indy on the same chart has enabled events for create/delete/mouse-move, just skip this events up front because they aren't
   //    needed, AND in the worst case, this indy might cause MT4 to hang!!  Skipping the events seems to help, along with other (major) changes to the code below.
   if(id==CHARTEVENT_OBJECT_CREATE || id==CHARTEVENT_OBJECT_DELETE) return; // This appears to make this indy compatible with other programs that enabled CHART_EVENT_OBJECT_CREATE and/or CHART_EVENT_OBJECT_DELETE
   if(id==CHARTEVENT_MOUSE_MOVE    || id==CHARTEVENT_MOUSE_WHEEL)   return; // If this, or another program, enabled mouse-events, these are not needed below, so skip it unless actually needed. 

   static string prevState ="";
   if (id==CHARTEVENT_OBJECT_CLICK && sparam==buttonId)
   {
      string newState = GetButtonState(buttonId);
         if (newState!=prevState)
         if (newState=="off")
                  { 
                    ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_OFF_color);
                    for (int Forex=0; Forex<indicator_buffers; Forex++) SetIndexStyle(Forex,DRAW_NONE);
                    deinit2();
                    restoreOriginalColors();
                    prevState=newState; 
                  }
            else  { 
                    ObjectSetInteger(0,buttonId,OBJPROP_COLOR,btn_text_ON_color); 
                    init2();
                    ConfigureChartAppearance();
                    prevState=newState; 
                  }
            ObjectSetString(ChartID(),buttonId,OBJPROP_TEXT,btn_text);
   }
}  
//Forex-Station button template end42; copy and paste
//+------------------------------------------------------------------------------------------------------------------+
void ConfigureChartAppearance()
{
   //--- Apply General Chart Settings
   ChartSetInteger(0, CHART_MODE, CHART_CANDLES);
   ChartSetInteger(0, CHART_SHOW_GRID, new_CHART_SHOW_GRID);
   
   //--- Apply Colors from User Inputs
   ChartSetInteger(0, CHART_COLOR_BACKGROUND,  new_CHART_COLOR_BACKGROUND);
   ChartSetInteger(0, CHART_COLOR_FOREGROUND,  new_CHART_COLOR_FOREGROUND);
   
   //--- Apply Candle Body Colors
   ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, new_CHART_COLOR_CANDLE_BULL);
   ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, new_CHART_COLOR_CANDLE_BEAR);
   
   //--- Apply Wick and Outline Colors
   ChartSetInteger(0, CHART_COLOR_CHART_UP,    new_CHART_COLOR_CHART_UP);
   ChartSetInteger(0, CHART_COLOR_CHART_DOWN,  new_CHART_COLOR_CHART_DOWN);
   
   //--- Refresh the chart to show changes immediately
   ChartRedraw();
}
//+------------------------------------------------------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Function to restore original colors                              |
//+------------------------------------------------------------------+
void restoreOriginalColors()
{
//   if(colorsCaptured)
   {
      ChartSetInteger(0, CHART_COLOR_BACKGROUND,  (color)old_bg);
      ChartSetInteger(0, CHART_COLOR_FOREGROUND,  (color)old_fg);
      ChartSetInteger(0, CHART_COLOR_GRID,        (color)old_grid);
      ChartSetInteger(0, CHART_COLOR_CHART_UP,    (color)old_up);
      ChartSetInteger(0, CHART_COLOR_CHART_DOWN,  (color)old_down);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, (color)old_bull);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, (color)old_bear);
      
      ChartRedraw();
   }
}
//+------------------------------------------------------------------------------------------------------------------+
int init2()
{
   //--- Bind arrays to buffers
   SetIndexBuffer(0, shortTermLows);
   SetIndexBuffer(1, shortTermHighs);
   SetIndexBuffer(2, intermediateTermLows);
   SetIndexBuffer(3, intermediateTermHighs);
   SetIndexBuffer(4, longTermLows);
   SetIndexBuffer(5, longTermHighs);

   //--- Configure Plots (MQL4 Equivalent of PlotIndexSetInteger)
   for(int Banzai=0; Banzai<indicator_buffers; Banzai++) {
      SetIndexStyle(Banzai, DRAW_ARROW);
      SetIndexEmptyValue(Banzai, EMPTY_VALUE);
   }

   // Set specific arrows for Long Term (MT5 codes 233/234)
   SetIndexArrow(0, ShortTermUpArrowCode);
   SetIndexArrow(1, ShortTermDownArrowCode);
   SetIndexArrow(2, IntermediateTermUpArrowCode);
   SetIndexArrow(3, IntermediateTermDownArrowCode);
   SetIndexArrow(4, LongTermUpArrowCode);
   SetIndexArrow(5, LongTermDownArrowCode);

//   ConfigureChartAppearance();
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------------------------------------------------------+
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[])
{
   //--- Recalculate on new bar or first run
   if(prev_calculated < rates_total)
   {
      ArrayInitialize(shortTermLows,  EMPTY_VALUE);
      ArrayInitialize(shortTermHighs, EMPTY_VALUE);
      ArrayInitialize(intermediateTermLows,  EMPTY_VALUE);
      ArrayInitialize(intermediateTermHighs, EMPTY_VALUE);
      ArrayInitialize(longTermLows,  EMPTY_VALUE);
      ArrayInitialize(longTermHighs, EMPTY_VALUE);

      //--- Step 1: Identify Short-Term (MT5 logic: Loop 1 to total-2)
      for(int i = 1; i < rates_total - 1; i++)
      {
         if(Low[i] < Low[i-1] && Low[i] < Low[i+1])
            shortTermLows[i] = Low[i];
         
         if(High[i] > High[i-1] && High[i] > High[i+1])
            shortTermHighs[i] = High[i];
      }

      //--- Step 2: Build Hierarchies (Direct clones of your MT5 Utility Functions)
      BuildIntermediateLows(shortTermLows, rates_total, intermediateTermLows);
      BuildIntermediateHighs(shortTermHighs, rates_total, intermediateTermHighs);
      
      //--- Step 3: Build Long-Term from Intermediate
      BuildIntermediateLows(intermediateTermLows, rates_total, longTermLows);
      BuildIntermediateHighs(intermediateTermHighs, rates_total, longTermHighs);

      // 4. Trend Logic
      if(EnableTrendCandleColoring) {
         UpdateTrend(rates_total);
      }

      // 5. Visibility Toggle
      ApplyVisibility(rates_total);
   }

   return(rates_total);
}
//+------------------------------------------------------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit2()
{
   //--- Restore the user's original chart colors captured in OnInit
   ChartSetInteger(0, CHART_COLOR_BACKGROUND,  (color)old_bg);
   ChartSetInteger(0, CHART_COLOR_FOREGROUND,  (color)old_fg);
   ChartSetInteger(0, CHART_COLOR_GRID,        (color)old_grid);
   
   //--- Restore the bar and wick colors
   ChartSetInteger(0, CHART_COLOR_CHART_UP,    (color)old_up);
   ChartSetInteger(0, CHART_COLOR_CHART_DOWN,  (color)old_down);
   
   //--- Restore the candle body fill colors
   ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, (color)old_bull);
   ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, (color)old_bear);
   
   //--- Force the chart to refresh visually
   ChartRedraw();
   
   //--- Optional: Print to terminal for confirmation
   Print("Larry Williams Indicator removed: Original chart colors restored.");
   return(0);
}
//+------------------------------------------------------------------------------------------------------------------+
void UpdateTrend(int total) 
{
      ChartSetInteger(0, CHART_COLOR_BACKGROUND, (color)old_bg);
      ChartSetInteger(0, CHART_COLOR_FOREGROUND, (color)old_fg);
      ChartSetInteger(0, CHART_COLOR_GRID,       (color)old_grid);
      ChartSetInteger(0, CHART_COLOR_CHART_UP,   (color)old_up);
      ChartSetInteger(0, CHART_COLOR_CHART_DOWN, (color)old_down);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BULL,(color)old_bull);
      ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR,(color)old_bear);
      ChartRedraw();
}
//+------------------------------------------------------------------------------------------------------------------+
void ApplyVisibility(int total) {
   if(!DisplayShortTermArrows) { for(int i=0; i<total; i++) { shortTermLows[i]=EMPTY_VALUE; shortTermHighs[i]=EMPTY_VALUE; } }
   if(!DisplayIntermediateTermArrows) { for(int i=0; i<total; i++) { intermediateTermLows[i]=EMPTY_VALUE; intermediateTermHighs[i]=EMPTY_VALUE; } }
   if(!DisplayLongTermArrows) { for(int i=0; i<total; i++) { longTermLows[i]=EMPTY_VALUE; longTermHighs[i]=EMPTY_VALUE; } }
}
//+------------------------------------------------------------------------------------------------------------------+
void BuildIntermediateHighs(const double &src[], int total, double &dst[])
{
   int idx[]; ArrayResize(idx, 0);
   for(int i=0; i<total; i++) {
      if(src[i] != EMPTY_VALUE) {
         int len = ArraySize(idx);
         ArrayResize(idx, len+1);
         idx[len] = i;
      }
   }
   if(ArraySize(idx) < 3) return;
   for(int k=1; k<ArraySize(idx)-1; k++) {
      int prev = idx[k-1], cur = idx[k], next = idx[k+1];
      if(src[cur] > src[prev] && src[cur] > src[next]) dst[cur] = src[cur];
   }
}
//+------------------------------------------------------------------------------------------------------------------+
void BuildIntermediateLows(const double &src[], int total, double &dst[])
{
   int idx[]; ArrayResize(idx, 0);
   for(int i=0; i<total; i++) {
      if(src[i] != EMPTY_VALUE) {
         int len = ArraySize(idx);
         ArrayResize(idx, len+1);
         idx[len] = i;
      }
   }
   if(ArraySize(idx) < 3) return;
   for(int k=1; k<ArraySize(idx)-1; k++) {
      int prev = idx[k-1], cur = idx[k], next = idx[k+1];
      if(src[cur] < src[prev] && src[cur] < src[next]) dst[cur] = src[cur];
   }
}
//+------------------------------------------------------------------------------------------------------------------+
