//+------------------------------------------------------------------+
//|                                        Average_Daily_Range.mq5     |
//|                                                                    |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property version   "1.00"
#property indicator_chart_window

//--- input parameters
input int      ADR_Period = 14;           // ADR Period
input color    RangeColor = clrBlack;     // Default Range Color
input color    ReachedColor = clrWhite;   // Color when ADR is reached
input int      LineWidth  = 2;            // Line Width
input          ENUM_LINE_STYLE LineStyle = STYLE_SOLID; // Line Style
input color    TextColor  = clrTurquoise;     // Text Color
input bool     ShowADRText = true;        // Show ADR Panel
input bool     ShowADRLabels = true;      // Show ADR Line Labels
input int      LabelOffset = 1700;        // Label X-Offset (bars from start)
input          ENUM_BASE_CORNER  PanelCorner = CORNER_RIGHT_UPPER;  // Panel Position
input int      PanelXOffset = 14;         // Panel X Offset from corner
input int      PanelYOffset = 15;         // Panel Y Offset from corner

//--- Global variables
datetime       LastCalculatedDay = 0;
double         LastADR = 0;
double         LockedUpperADR = 0;
double         LockedLowerADR = 0;
bool           ADRLocked = false;  // Single lock flag since both lines lock together
string         IndicatorName = "ADR";
string         LastSymbol = "";
static         ENUM_TIMEFRAMES lastTimeframe = PERIOD_CURRENT;
static bool    lines_created = false;
double         last_adr = 0;              // Store last calculated ADR value

//+------------------------------------------------------------------+
//| Custom indicator initialization function                           |
//+------------------------------------------------------------------+
int OnInit()
{
   ObjectsDeleteAll(0, IndicatorName + "_");
   
   // Reset all state variables
   LastCalculatedDay = 0;
   LastADR = 0;
   LockedUpperADR = 0;
   LockedLowerADR = 0;
   ADRLocked = false;
   lines_created = false;
   last_adr = 0;
   LastSymbol = _Symbol;
   lastTimeframe = Period();
   
   ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, true);
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Calculate ADR                                                      |
//+------------------------------------------------------------------+
double CalculateADR(const datetime &time[], const double &high[], const double &low[], int rates_total)
{
    static MqlRates rates[];
    
    if(CopyRates(_Symbol, PERIOD_D1, 1, ADR_Period, rates) <= 0)
        return 0;
        
    double total_range = 0;
    int days_counted = 0;
    datetime today_start = iTime(_Symbol, PERIOD_D1, 0);
    
    for(int i = 0; i < ADR_Period && i < ArraySize(rates); i++)
    {
        if(rates[i].time >= today_start)
            continue;
            
        total_range += (rates[i].high - rates[i].low) / _Point;
        days_counted++;
    }
    
    return (days_counted > 0) ? total_range / days_counted : 0;
}

//+------------------------------------------------------------------+
//| Update ADR Panel                                                   |
//+------------------------------------------------------------------+
void UpdateADRPanel(double adr)
{
   static double last_panel_adr = 0;
   static int last_corner = -1;
   static int last_x_offset = -1;
   static int last_y_offset = -1;
   
   // Convert ADR from points to pips and round up
   int adr_pips = (int)MathCeil(adr / 10);
   int adr_3x_pips = (int)MathCeil((adr * 3) / 10);
   
   if(!ShowADRText || 
      (adr == last_panel_adr && 
       last_corner == PanelCorner && 
       last_x_offset == PanelXOffset && 
       last_y_offset == PanelYOffset)) 
       return;
   
   last_panel_adr = adr;
   last_corner = PanelCorner;
   last_x_offset = PanelXOffset;
   last_y_offset = PanelYOffset;
   
   const int panelWidth = 100;
   const int panelHeight = 65;
   const int textLeftMargin = 13;
   const int textTopMargin1 = 11;
   const int textTopMargin2 = 38;
   
   int panelX = 0, panelY = 0;
   int textX = 0;
   int textY1 = 0, textY2 = 0;
   
   switch(PanelCorner)
   {
      case CORNER_LEFT_UPPER:
         panelX = PanelXOffset;
         panelY = PanelYOffset;
         textX = PanelXOffset + textLeftMargin;
         textY1 = PanelYOffset + textTopMargin1;
         textY2 = PanelYOffset + textTopMargin2;
         break;
         
      case CORNER_RIGHT_UPPER:
         panelX = PanelXOffset + panelWidth;
         panelY = PanelYOffset;
         textX = PanelXOffset + panelWidth - textLeftMargin;
         textY1 = PanelYOffset + textTopMargin1;
         textY2 = PanelYOffset + textTopMargin2;
         break;
         
      case CORNER_LEFT_LOWER:
         panelX = PanelXOffset;
         panelY = PanelYOffset + panelHeight;
         textX = PanelXOffset + textLeftMargin;
         textY1 = PanelYOffset + panelHeight - textTopMargin1;
         textY2 = PanelYOffset + panelHeight - textTopMargin2;
         break;
         
      case CORNER_RIGHT_LOWER:
         panelX = PanelXOffset + panelWidth;
         panelY = PanelYOffset + panelHeight;
         textX = PanelXOffset + panelWidth - textLeftMargin;
         textY1 = PanelYOffset + panelHeight - textTopMargin1;
         textY2 = PanelYOffset + panelHeight - textTopMargin2;
         break;
   }
   
   string rectName = IndicatorName + "_Background";
   if(ObjectFind(0, rectName) < 0)
      ObjectCreate(0, rectName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   
   ObjectSetInteger(0, rectName, OBJPROP_CORNER, PanelCorner);
   ObjectSetInteger(0, rectName, OBJPROP_XDISTANCE, panelX);
   ObjectSetInteger(0, rectName, OBJPROP_YDISTANCE, panelY);
   ObjectSetInteger(0, rectName, OBJPROP_XSIZE, panelWidth);
   ObjectSetInteger(0, rectName, OBJPROP_YSIZE, panelHeight);
   ObjectSetInteger(0, rectName, OBJPROP_BGCOLOR, C'129,129,129');
   ObjectSetInteger(0, rectName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, rectName, OBJPROP_COLOR, clrNONE);
   ObjectSetInteger(0, rectName, OBJPROP_ZORDER, 998);
   
   string adrName = IndicatorName + "_Text";
   if(ObjectFind(0, adrName) < 0)
      ObjectCreate(0, adrName, OBJ_LABEL, 0, 0, 0);
   
   ObjectSetInteger(0, adrName, OBJPROP_CORNER, PanelCorner);
   ObjectSetInteger(0, adrName, OBJPROP_XDISTANCE, textX);
   ObjectSetInteger(0, adrName, OBJPROP_YDISTANCE, textY1);
   ObjectSetInteger(0, adrName, OBJPROP_COLOR, TextColor);
   ObjectSetString(0, adrName, OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, adrName, OBJPROP_FONTSIZE, 9);
   ObjectSetInteger(0, adrName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
   ObjectSetInteger(0, adrName, OBJPROP_ZORDER, 999);
   ObjectSetString(0, adrName, OBJPROP_TEXT, "ADR     " + IntegerToString(adr_pips));  // Display pips
   
   string adr3xName = IndicatorName + "_3X_Text";
   if(ObjectFind(0, adr3xName) < 0)
      ObjectCreate(0, adr3xName, OBJ_LABEL, 0, 0, 0);
   
   ObjectSetInteger(0, adr3xName, OBJPROP_CORNER, PanelCorner);
   ObjectSetInteger(0, adr3xName, OBJPROP_XDISTANCE, textX);
   ObjectSetInteger(0, adr3xName, OBJPROP_YDISTANCE, textY2);
   ObjectSetInteger(0, adr3xName, OBJPROP_COLOR, TextColor);
   ObjectSetString(0, adr3xName, OBJPROP_FONT, "Arial");
   ObjectSetInteger(0, adr3xName, OBJPROP_FONTSIZE, 9);
   ObjectSetInteger(0, adr3xName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
   ObjectSetInteger(0, adr3xName, OBJPROP_ZORDER, 999);
   ObjectSetString(0, adr3xName, OBJPROP_TEXT, "ADRx3  " + IntegerToString(adr_3x_pips));  // Display pips
   
   ObjectSetInteger(0, rectName, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
   ObjectSetInteger(0, adrName, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
   ObjectSetInteger(0, adr3xName, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
}
//+------------------------------------------------------------------+
//| Draw ADR Lines                                                     |
//+------------------------------------------------------------------+
void DrawADRLines(datetime dayStart, double upperLevel, double lowerLevel)
{
    string upperName = IndicatorName + "_Upper_Line";
    string lowerName = IndicatorName + "_Lower_Line";
    
    // Force color based on locked state
    color currentColor = ADRLocked ? ReachedColor : RangeColor;
    
    if(!lines_created)
    {
        ObjectCreate(0, upperName, OBJ_TREND, 0, dayStart, upperLevel, dayStart + PeriodSeconds(PERIOD_D1), upperLevel);
        ObjectCreate(0, lowerName, OBJ_TREND, 0, dayStart, lowerLevel, dayStart + PeriodSeconds(PERIOD_D1), lowerLevel);
        lines_created = true;
        
        // Set static properties
        string lines[2] = {upperName, lowerName};
        for(int i=0; i<2; i++)
        {
            ObjectSetInteger(0, lines[i], OBJPROP_STYLE, LineStyle);
            ObjectSetInteger(0, lines[i], OBJPROP_WIDTH, LineWidth);
            ObjectSetInteger(0, lines[i], OBJPROP_RAY_RIGHT, true);
            ObjectSetInteger(0, lines[i], OBJPROP_BACK, false);
            ObjectSetInteger(0, lines[i], OBJPROP_SELECTABLE, false);
            ObjectSetString(0, lines[i], OBJPROP_TOOLTIP, "\n");
            ObjectSetInteger(0, lines[i], OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
        }
    }
    
    // Update positions and colors
    ObjectMove(0, upperName, 0, dayStart, upperLevel);
    ObjectMove(0, upperName, 1, dayStart + PeriodSeconds(PERIOD_D1), upperLevel);
    ObjectMove(0, lowerName, 0, dayStart, lowerLevel);
    ObjectMove(0, lowerName, 1, dayStart + PeriodSeconds(PERIOD_D1), lowerLevel);
    
    ObjectSetInteger(0, upperName, OBJPROP_COLOR, currentColor);
    ObjectSetInteger(0, lowerName, OBJPROP_COLOR, currentColor);
    
    // Update labels if enabled
    if(ShowADRLabels)
    {
        datetime labelTime = dayStart + (LabelOffset * 60);
        string upperLabel = IndicatorName + "_High_Label";
        string lowerLabel = IndicatorName + "_Low_Label";
        
        if(ObjectFind(0, upperLabel) < 0)
            ObjectCreate(0, upperLabel, OBJ_TEXT, 0, labelTime, upperLevel);
        if(ObjectFind(0, lowerLabel) < 0)
            ObjectCreate(0, lowerLabel, OBJ_TEXT, 0, labelTime, lowerLevel);
        
        ObjectSetInteger(0, upperLabel, OBJPROP_TIME, labelTime);
        ObjectSetInteger(0, upperLabel, OBJPROP_ANCHOR, ANCHOR_UPPER);
        ObjectSetDouble(0, upperLabel, OBJPROP_PRICE, upperLevel);
        ObjectSetString(0, upperLabel, OBJPROP_TEXT, "ADR High " + DoubleToString(upperLevel, _Digits));
        ObjectSetInteger(0, upperLabel, OBJPROP_COLOR, currentColor);
        ObjectSetInteger(0, upperLabel, OBJPROP_FONTSIZE, 8);
        ObjectSetInteger(0, upperLabel, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
        
        ObjectSetInteger(0, lowerLabel, OBJPROP_TIME, labelTime);
        ObjectSetInteger(0, lowerLabel, OBJPROP_ANCHOR, ANCHOR_UPPER);
        ObjectSetDouble(0, lowerLabel, OBJPROP_PRICE, lowerLevel);
        ObjectSetString(0, lowerLabel, OBJPROP_TEXT, "ADR Low " + DoubleToString(lowerLevel, _Digits));
        ObjectSetInteger(0, lowerLabel, OBJPROP_COLOR, currentColor);
        ObjectSetInteger(0, lowerLabel, OBJPROP_FONTSIZE, 8);
        ObjectSetInteger(0, lowerLabel, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
    }
}

//+------------------------------------------------------------------+
//| 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(rates_total < ADR_Period) return(0);
    
    datetime currentDayStart = iTime(_Symbol, PERIOD_D1, 0);
    
    // Check for symbol or timeframe change
    if(LastSymbol != _Symbol || lastTimeframe != Period())
    {
        Print("=== RESET: Symbol/Timeframe Change ===");
        LastSymbol = _Symbol;
        lastTimeframe = Period();
        LastCalculatedDay = 0;
        lines_created = false;
        ADRLocked = false;
        ObjectsDeleteAll(0, IndicatorName + "_");
    }
    
    // If it's a new day, perform initialization
    if(LastCalculatedDay != currentDayStart)
    {
        Print("=== NEW DAY STARTED ===");
        LastCalculatedDay = currentDayStart;
        ADRLocked = false;
        LockedUpperADR = 0;
        LockedLowerADR = 0;
        LastADR = CalculateADR(time, high, low, rates_total);
        
        if(LastADR != last_adr)
        {
            UpdateADRPanel(LastADR);
            last_adr = LastADR;
        }
        
        // Check historical data for current day
        MqlRates today_rates[];
        if(CopyRates(_Symbol, PERIOD_M1, currentDayStart, TimeCurrent(), today_rates) > 0)
        {
            double running_high = today_rates[0].high;
            double running_low = today_rates[0].low;
            double adr_points = LastADR * _Point;
            
            // Scan through minutes chronologically until first contact
            for(int i = 1; i < ArraySize(today_rates); i++)
            {
                // Update running high/low
                if(today_rates[i].high > running_high) running_high = today_rates[i].high;
                if(today_rates[i].low < running_low) running_low = today_rates[i].low;
                
                // Calculate ADR levels based on current running high/low
                double current_upper = running_low + adr_points;
                double current_lower = running_high - adr_points;
                
                // Check if this bar touched either level
                if(today_rates[i].high >= current_upper || today_rates[i].low <= current_lower)
                {
                    Print("=== INITIAL ADR CONTACT DETECTED ===");
                    Print("Contact Time: ", today_rates[i].time);
                    Print("Running High at Contact: ", running_high);
                    Print("Running Low at Contact: ", running_low);
                    
                    ADRLocked = true;
                    LockedUpperADR = current_upper;
                    LockedLowerADR = current_lower;
                    
                    Print("Locked Upper ADR: ", LockedUpperADR);
                    Print("Locked Lower ADR: ", LockedLowerADR);
                    
                    // Draw lines and return since we're locked
                    DrawADRLines(currentDayStart, LockedUpperADR, LockedLowerADR);
                    return(rates_total);
                }
            }
        }
    }
    
    // If ADR is already locked, just redraw lines and return
    if(ADRLocked)
    {
        DrawADRLines(currentDayStart, LockedUpperADR, LockedLowerADR);
        return(rates_total);
    }
    
    // If we get here, ADR is not locked, so calculate current levels
    double today_high = iHigh(_Symbol, PERIOD_D1, 0);
    double today_low = iLow(_Symbol, PERIOD_D1, 0);
    double adr_points = LastADR * _Point;
    
    double upper_adr = today_low + adr_points;
    double lower_adr = today_high - adr_points;
    
    // Check for real-time contact with either line
    if(today_high >= upper_adr || today_low <= lower_adr)
    {
        Print("\n!!! ADR CONTACT - LOCKING LEVELS !!!");
        Print("Values at Contact:");
        Print("Upper ADR: ", upper_adr);
        Print("Lower ADR: ", lower_adr);
        
        ADRLocked = true;
        LockedUpperADR = upper_adr;
        LockedLowerADR = lower_adr;
    }
    
    // Draw the current lines (locked or unlocked)
    DrawADRLines(currentDayStart, ADRLocked ? LockedUpperADR : upper_adr, 
                               ADRLocked ? LockedLowerADR : lower_adr);
    
    return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    static datetime last_update_time = 0;
    
    if(id == CHARTEVENT_CHART_CHANGE)
    {
        datetime current_time = TimeCurrent();
        if(current_time - last_update_time >= 1)
        {
            UpdateADRPanel(last_adr);
            last_update_time = current_time;
        }
    }
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, IndicatorName + "_");
}