//+------------------------------------------------------------------+
//|                                        DrawdownTimelineChart.mqh |
//+------------------------------------------------------------------+
#ifndef DRAWDOWNTIMELINECHART_MQH
#define DRAWDOWNTIMELINECHART_MQH

#include <Canvas\Canvas.mqh>
#include "DrawdownTypes.mqh"

//+------------------------------------------------------------------+
//| CDrawdownTimelineChart                                           |
//| Renders the equity curve as a line, with each drawdown episode   |
//| shaded as a translucent band annotated with its depth and        |
//| duration, using a CCanvas panel.                                 |
//+------------------------------------------------------------------+
class CDrawdownTimelineChart
  {
private:
   CCanvas               m_canvas;
   string                m_object_name;
   bool                  m_created;
   string                m_font_name;
   int                   m_font_size;
   uint                  m_font_flags;

   int                   FindIndexForTime(const CEquityPoint &curve[], int count, datetime t) const;

public:
                     CDrawdownTimelineChart(void);
                    ~CDrawdownTimelineChart(void);

   bool                  Draw(const CEquityPoint &curve[], int curve_count,
                              const CDrawdownEpisode &episodes[], int episode_count,
                              int x, int y, int width, int height);
   void                  Clear(void);
  };

//+------------------------------------------------------------------+
//| Constructor: assigns a unique object name, fixes the font used   |
//| for both drawing and measuring text, and marks the panel as not  |
//| yet created.                                                     |
//+------------------------------------------------------------------+
CDrawdownTimelineChart::CDrawdownTimelineChart(void)
  {
   m_object_name = "DrawdownTimelinePanel";
   m_created     = false;
   m_font_name   = "Arial";
   m_font_size   = 14;      // <-- edit this to change the annotation text size
   m_font_flags  = FW_BOLD; // <-- edit/remove this to change the annotation weight
  }

//+------------------------------------------------------------------+
//| Destructor: intentionally does not remove the canvas panel. The  |
//| panel is a chart object owned by the chart itself, and must      |
//| remain visible after the CDrawdownTimelineChart instance that    |
//| drew it goes out of scope at the end of a script's OnStart.      |
//+------------------------------------------------------------------+
CDrawdownTimelineChart::~CDrawdownTimelineChart(void)
  {
  }

//+------------------------------------------------------------------+
//| Draw                                                             |
//| Maps the equity curve and drawdown episodes onto the panel,      |
//| shades each episode's time span as a translucent band, draws the |
//| equity curve as a connected line on top, and annotates each band |
//| with its depth percentage and duration in days.                  |
//+------------------------------------------------------------------+
bool CDrawdownTimelineChart::Draw(const CEquityPoint &curve[], int curve_count,
                                  const CDrawdownEpisode &episodes[], int episode_count,
                                  int x, int y, int width, int height)
  {
//--- remove any previously drawn panel before creating a new one
   Clear();
   if(!m_canvas.CreateBitmapLabel(m_object_name, x, y, width, height, COLOR_FORMAT_ARGB_NORMALIZE))
     {
      ::Print("DrawdownDashboard: CreateBitmapLabel failed, error ", ::GetLastError());
      return(false);
     }
   m_created = true;
//--- fix a known font for both drawing and measurement, so the two never disagree
   m_canvas.FontSet(m_font_name, m_font_size, m_font_flags);
   ::TextSetFont(m_font_name, m_font_size, m_font_flags);
   m_canvas.Erase(::ColorToARGB(clrWhiteSmoke, 255));
   if(curve_count < 2)
     {
      m_canvas.Update();
      return(true);
     }
//--- find the equity range across the curve, with a small padding margin
   double min_eq = curve[0].equity;
   double max_eq = curve[0].equity;
   for(int i = 1; i < curve_count; i++)
     {
      if(curve[i].equity < min_eq)
         min_eq = curve[i].equity;
      if(curve[i].equity > max_eq)
         max_eq = curve[i].equity;
     }
   double eq_range = max_eq - min_eq;
   if(eq_range <= 0.0)
      eq_range = 1.0;
   double eq_pad = eq_range * 0.1;
   min_eq -= eq_pad;
   max_eq += eq_pad;
   eq_range = max_eq - min_eq;
//--- map the curve's point index, not elapsed calendar time, to the
//--- horizontal drawing area. Mapping by index rather than by time
//--- keeps a long quiet stretch from squeezing a burst of recent
//--- activity into a sliver of the panel; the exact duration in days
//--- is still shown in each annotation and in the terminal log table.
   int last_index   = curve_count - 1;
//--- reserve two stacked rows for annotation text, regardless of how
//--- many days the lookback spans; alternating rows below guarantees
//--- two adjacent episodes never share a row, so their labels cannot
//--- collide even when the episodes themselves sit close together
   int line_height  = m_font_size + 6;
   int chart_top    = 10 + (2 * line_height);
   int chart_bottom = height - 20;
   int chart_left   = 10;
   int chart_right  = width - 10;
//--- draw a shaded band and annotation for each drawdown episode
   for(int i = 0; i < episode_count; i++)
     {
      int start_index     = FindIndexForTime(curve, curve_count, episodes[i].start_time);
      int recovery_index  = FindIndexForTime(curve, curve_count, episodes[i].recovery_time);
      int bx1 = chart_left + (int)(((double)start_index    / last_index) * (chart_right - chart_left));
      int bx2 = chart_left + (int)(((double)recovery_index / last_index) * (chart_right - chart_left));
      m_canvas.FillRectangle(bx1, chart_top, bx2, chart_bottom, ::ColorToARGB(clrCrimson, 60));
      //--- build the annotation text and measure it before centering
      string tag = ::DoubleToString(episodes[i].depth_percent, 1) + "% / " +
                   ::DoubleToString(episodes[i].duration_days, 0) + "d";
      if(episodes[i].is_open)
         tag = tag + " (open)";
      uint measured_w = 0;
      uint measured_h = 0;
      ::TextGetSize(tag, measured_w, measured_h);
      int mid    = (bx1 + bx2) / 2;
      int row    = i % 2;
      int text_y = 6 + (row * line_height);
      m_canvas.TextOut(mid - (int)measured_w / 2, text_y, tag, ::ColorToARGB(clrBlack, 255));
     }
//--- draw the equity curve as a series of connected line segments,
//--- spacing each point evenly by index rather than by elapsed time
   int prev_x = 0;
   int prev_y = 0;
   for(int i = 0; i < curve_count; i++)
     {
      int px = chart_left + (int)(((double)i / last_index) * (chart_right - chart_left));
      int py = chart_bottom - (int)(((curve[i].equity - min_eq) / eq_range) * (chart_bottom - chart_top));
      if(i > 0)
         m_canvas.Line(prev_x, prev_y, px, py, ::ColorToARGB(clrDarkSlateBlue, 255));
      prev_x = px;
      prev_y = py;
     }
   m_canvas.Update();
   return(true);
  }

//+------------------------------------------------------------------+
//| FindIndexForTime                                                 |
//| Returns the index of the curve point matching the given time.    |
//| Episode boundary times always originate from an actual curve     |
//| point, so an exact match is expected; falls back to the last     |
//| index if no match is found.                                      |
//+------------------------------------------------------------------+
int CDrawdownTimelineChart::FindIndexForTime(const CEquityPoint &curve[], int count, datetime t) const
  {
   for(int i = 0; i < count; i++)
     {
      if(curve[i].time == t)
         return(i);
     }
   return(count - 1);
  }

//+------------------------------------------------------------------+
//| Clear                                                            |
//| Removes the canvas panel from the chart if it was created.       |
//+------------------------------------------------------------------+
void CDrawdownTimelineChart::Clear(void)
  {
   if(!m_created)
      return;
   m_canvas.Destroy();
   m_created = false;
  }

#endif // DRAWDOWNTIMELINECHART_MQH
//+------------------------------------------------------------------+