//+------------------------------------------------------------------+
//|                                       CalendarSummaryPrinter.mqh |
//+------------------------------------------------------------------+
#ifndef CALENDARSUMMARYPRINTER_MQH
#define CALENDARSUMMARYPRINTER_MQH

#include "CalendarTypes.mqh"

//+---------------------------------------------------------------------+
//| CCalendarSummaryPrinter                                             |
//| Complements the visual heatmap with a numerical weekday summary and |
//| an explicit best-day and worst-day report, printed to the terminal  |
//| log. A heatmap is excellent for spotting clusters and streaks at a  |
//| glance, but a trader often also wants a precise number: exactly     |
//| how much was made or lost on Mondays in aggregate, or exactly which |
//| single date was the best of the period. This class exists to answer |
//| those precise questions without requiring the reader to hover over  |
//| or manually tally cells in the rendered grid.                       |
//+---------------------------------------------------------------------+
class CCalendarSummaryPrinter
  {
private:
   string            WeekdayName(int weekday_row) const;

public:
                     CCalendarSummaryPrinter(void);
                    ~CCalendarSummaryPrinter(void);
   void              Print(const CDailyRecord &days[], int count);
  };

//+--------------------------------------------------------------------+
//| Constructor                                                        |
//| The printer holds no state between calls, so construction performs |
//| no work beyond default object creation.                            |
//+--------------------------------------------------------------------+
CCalendarSummaryPrinter::CCalendarSummaryPrinter(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//| No resources are owned by this class, so no cleanup is required. |
//+------------------------------------------------------------------+
CCalendarSummaryPrinter::~CCalendarSummaryPrinter(void)
  {
  }

//+----------------------------------------------------------------------+
//| WeekdayName                                                          |
//| Returns the full weekday name for a weekday row index, following the |
//| same Sunday-is-zero convention used throughout the rest of the       |
//| project. Returns "Unknown" for any out-of-range index as a defensive |
//| fallback; this branch should never be reached in normal operation.   |
//+----------------------------------------------------------------------+
string CCalendarSummaryPrinter::WeekdayName(const int weekday_row) const
  {
   string names[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
   if(weekday_row < 0 || weekday_row > 6)
      return("Unknown");
   return(names[weekday_row]);
  }

//+------------------------------------------------------------------------+
//| Print                                                                  |
//| Prints a Sunday-through-Saturday weekday breakdown, followed by the    |
//| single best and single worst calendar dates by daily P&L. The weekday  |
//| totals are seeded at zero for all seven weekdays before any record is  |
//| examined, so every weekday appears in the report even if the selected  |
//| range contains no trading activity at all on that weekday; the report  |
//| explicitly says "no records" in that case rather than silently omitting|
//| the row. A weekday whose total P&L nets to exactly zero is reported as |
//| "breakeven," never folded into either the gain or the loss category.   |
//| When the supplied array is empty, the weekday table is still printed   |
//| with every weekday at zero, and the best/worst section is skipped with |
//| an explicit message instead of reading past the end of the array.      |
//+------------------------------------------------------------------------+
void CCalendarSummaryPrinter::Print(const CDailyRecord &days[],const int count)
  {
//--- seed per-weekday totals at zero regardless of whether any record exists
   double weekday_pnl[7];
   int    weekday_trade_count[7];
   int    weekday_day_count[7];
   for(int row = 0; row < 7; row++)
     {
      weekday_pnl[row]         = 0.0;
      weekday_trade_count[row] = 0;
      weekday_day_count[row]   = 0;
     }
//--- fold every daily record into its corresponding weekday bucket
   for(int i = 0; i < count; i++)
     {
      MqlDateTime time_parts;
      ::TimeToStruct(days[i].m_date, time_parts);
      int row = time_parts.day_of_week;
      weekday_pnl[row]         += days[i].m_daily_pnl;
      weekday_trade_count[row] += days[i].m_trade_count;
      weekday_day_count[row]   += 1;
     }
//--- print the weekday-level breakdown from Sunday through Saturday
   ::Print("Calendar Heatmap Weekday Summary");
   for(int row = 0; row < 7; row++)
     {
      string classification;
      //--- a weekday total of exactly zero is reported explicitly, never as a gain or a loss
      if(weekday_day_count[row] == 0)
         classification = "no records";
      else
         if(weekday_pnl[row] > 0.0)
            classification = "net gain";
         else
            if(weekday_pnl[row] < 0.0)
               classification = "net loss";
            else
               classification = "breakeven";
      ::PrintFormat("%-10s calendar dates: %3d  trades: %4d  daily P&L total: %12.2f  (%s)",
                    WeekdayName(row), weekday_day_count[row], weekday_trade_count[row],
                    weekday_pnl[row], classification);
     }
//--- identify the single best and single worst calendar dates by daily P&L
   if(count <= 0)
     {
      ::Print("No daily records are available in the selected range to determine best and worst days.");
      return;
     }
   int best_index  = 0;
   int worst_index = 0;
   for(int i = 1; i < count; i++)
     {
      if(days[i].m_daily_pnl > days[best_index].m_daily_pnl)
         best_index = i;
      if(days[i].m_daily_pnl < days[worst_index].m_daily_pnl)
         worst_index = i;
     }
   ::PrintFormat("Best calendar date by daily P&L:  %s   P&L: %.2f   trades: %d",
                 ::TimeToString(days[best_index].m_date, TIME_DATE), days[best_index].m_daily_pnl, days[best_index].m_trade_count);
   ::PrintFormat("Worst calendar date by daily P&L: %s   P&L: %.2f   trades: %d",
                 ::TimeToString(days[worst_index].m_date, TIME_DATE), days[worst_index].m_daily_pnl, days[worst_index].m_trade_count);
  }

#endif // CALENDARSUMMARYPRINTER_MQH
//+------------------------------------------------------------------+