//+------------------------------------------------------------------+
//|                                              SessionBarChart.mqh |
//+------------------------------------------------------------------+
#ifndef SESSIONBARCHART_MQH
#define SESSIONBARCHART_MQH

#include <Canvas\Canvas.mqh>
#include "SessionEnums.mqh"

//+------------------------------------------------------------------+
//| CSessionBarChart                                                 |
//| Renders session metrics as a horizontal, color-coded bar chart   |
//| on the chart using a CCanvas panel.                              |
//+------------------------------------------------------------------+
class CSessionBarChart
  {
private:
   CCanvas               m_canvas;
   string                m_object_name;
   bool                  m_created;
   string                m_font_name;
   int                   m_font_size;

public:
                     CSessionBarChart(void);
                    ~CSessionBarChart(void);

   bool                  Draw(const CSessionMetrics &metrics[], int count,
                              int total_trades, double total_win_rate, double total_net_pnl,
                              int x, int y, int width, int height);
   void                  Clear(void);
  };

//+------------------------------------------------------------------+
//| Constructor: assigns a unique object name for the canvas panel,  |
//| fixes the font used for both drawing and measuring text, and     |
//| marks the panel as not yet created.                              |
//+------------------------------------------------------------------+
CSessionBarChart::CSessionBarChart(void)
  {
   m_object_name = "SessionBarChartPanel";
   m_created     = false;
   m_font_name   = "Arial";
   m_font_size   = 16;
  }

//+------------------------------------------------------------------+
//| 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 CSessionBarChart instance that drew it  |
//| goes out of scope at the end of a script's OnStart.              |
//+------------------------------------------------------------------+
CSessionBarChart::~CSessionBarChart(void)
  {
  }

//+------------------------------------------------------------------+
//| Draw                                                             |
//| Renders one horizontal bar per session, proportional to net P&L  |
//| relative to the largest absolute P&L across all sessions, with   |
//| a vertical zero line, green or red coloring by profitability,    |
//| and a summary row at the bottom showing the account-wide total   |
//| trade count, win rate, and net P&L across every session. The     |
//| summary row's text is positioned using the actual measured       |
//| width of each string, not an estimated character width, so one   |
//| value can never overlap the next regardless of digit count.      |
//+------------------------------------------------------------------+
bool CSessionBarChart::Draw(const CSessionMetrics &metrics[], int count,
                            int total_trades, double total_win_rate, double total_net_pnl,
                            int x, int y, int width, int height)
  {
//--- removes any previously drawn panel before creating a new one
   Clear();
//--- creates the bitmap label that the canvas will draw onto
   if(!m_canvas.CreateBitmapLabel(m_object_name, x, y, width, height, COLOR_FORMAT_ARGB_NORMALIZE))
     {
      ::Print("SessionDashboard: 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);
   ::TextSetFont(m_font_name, m_font_size);
//--- clears the panel to a light background before drawing
   m_canvas.Erase(::ColorToARGB(clrWhiteSmoke, 255));
//--- finds the largest absolute net P&L among sessions with trades
   double max_abs_pnl = 0.0;
   for(int i = 0; i < count; i++)
     {
      if(metrics[i].trade_count == 0)
         continue;
      double abs_pnl = ::MathAbs(metrics[i].net_pnl);
      if(abs_pnl > max_abs_pnl)
         max_abs_pnl = abs_pnl;
     }
   if(max_abs_pnl <= 0.0)
      max_abs_pnl = 1.0;
//--- reserves one extra row at the bottom of the panel for the summary line
   int zero_x     = width / 2;
   int half_width = (width / 2) - 20;
   int row_height = height / (count + 1);
//--- draws the vertical zero line spanning only the session bar rows
   m_canvas.LineVertical(zero_x, 0, row_height * count, ::ColorToARGB(clrDarkGray, 255));
//--- draws one bar per session
   int      bar_y       = 0;
   int      bar_x       = 0;
   int      bar_width   = 0;
   uint     bar_color   = 0;
   string   pnl_text    = "";
   for(int i = 0; i < count; i++)
     {
      bar_y = i * row_height + (row_height / 4);
      //--- Uses green for profitable sessions and red for the rest
      bar_color = (metrics[i].net_pnl > 0.0) ? ::ColorToARGB(clrForestGreen, 255)
                  : ::ColorToARGB(clrCrimson, 255);
      //--- computes bar width proportional to this session's net P&L
      bar_width = (int)((::MathAbs(metrics[i].net_pnl) / max_abs_pnl) * half_width);
      //--- draws the bar extending right for profit, left for loss
      if(metrics[i].net_pnl > 0.0)
         bar_x = zero_x;
      else
         bar_x = zero_x - bar_width;
      m_canvas.FillRectangle(bar_x, bar_y, bar_x + bar_width, bar_y + (row_height / 2), bar_color);
      //--- draws the session name label on the left side of the panel
      m_canvas.TextOut(10, bar_y, metrics[i].name, ::ColorToARGB(clrBlack, 255));
      //--- draws the net P&L value on the right side of the panel
      pnl_text = ::DoubleToString(metrics[i].net_pnl, 2);
      m_canvas.TextOut(width - 90, bar_y, pnl_text, ::ColorToARGB(clrBlack, 255));
     }
//--- draw a divider line separating the bars from the summary row
   int summary_y = row_height * count;
   m_canvas.LineHorizontal(0, summary_y, width, ::ColorToARGB(clrDarkGray, 255));
//--- draws the three account-wide totals in sequence, each positioned after
//--- the ACTUAL measured width of the previous one plus a fixed gap, so a
//--- longer string can never overlap the one that follows it
   int    text_y       = summary_y + (row_height / 4);
   int    gap          = 16;
   uint    measured_w  = 0;
   uint    measured_h  = 0;
   string trades_text  = "Trades: " + ::IntegerToString(total_trades);
   string winrate_text = "Win rate: " + ::DoubleToString(total_win_rate, 1) + "%";
   string netpnl_text  = "Net P&L: " + ::DoubleToString(total_net_pnl, 2);
   int    trades_x     = 10;
   ::TextGetSize(trades_text, measured_w, measured_h);
   int    winrate_x    = trades_x + (int)measured_w + gap;
   ::TextGetSize(winrate_text, measured_w, measured_h);
   int    netpnl_x     = winrate_x + (int)measured_w + gap;
   m_canvas.TextOut(trades_x,  text_y, trades_text,  ::ColorToARGB(clrBlack, 255));
   m_canvas.TextOut(winrate_x, text_y, winrate_text, ::ColorToARGB(clrBlack, 255));
   m_canvas.TextOut(netpnl_x,  text_y, netpnl_text,  ::ColorToARGB(clrBlack, 255));
//--- commit the drawing to the chart
   m_canvas.Update();
   return(true);
  }

//+------------------------------------------------------------------+
//| Clear                                                            |
//| Removes the canvas panel from the chart if it was created.       |
//+------------------------------------------------------------------+
void CSessionBarChart::Clear(void)
  {
   if(!m_created)
      return;
   m_canvas.Destroy();
   m_created = false;
  }

#endif // SESSIONBARCHART_MQH
//+------------------------------------------------------------------+