//+------------------------------------------------------------------+
//|                                    CorrelationSummaryPrinter.mqh |
//+------------------------------------------------------------------+
#ifndef CORRELATIONSUMMARYPRINTER_MQH
#define CORRELATIONSUMMARYPRINTER_MQH

#include "CorrelationTypes.mqh"

//+--------------------------------------------------------------------+
//| CCorrelationSummaryPrinter                                         |
//| Complements the heatmap with an exact numerical report printed to  |
//| the Experts tab: which symbol pairs exceed the warning threshold,  |
//| which pairs could not be scored at all, and the single highest and |
//| single lowest defined correlation in the corr_matrix.              |
//+--------------------------------------------------------------------+
class CCorrelationSummaryPrinter
  {
public:
                     CCorrelationSummaryPrinter(void);
                    ~CCorrelationSummaryPrinter(void);
   void              Print(const CCorrelationMatrix &corr_matrix, double warning_threshold) const;
  };

//+-------------------------------------------------------------------+
//| Constructor                                                       |
//| The printer holds no state between calls, so construction performs|
//| no work beyond default object creation.                           |
//+-------------------------------------------------------------------+
CCorrelationSummaryPrinter::CCorrelationSummaryPrinter(void)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//| No resources are owned by this class, so no cleanup is required. |
//+------------------------------------------------------------------+
CCorrelationSummaryPrinter::~CCorrelationSummaryPrinter(void)
  {
  }

//+--------------------------------------------------------------------------+
//| Print                                                                    |
//| Scans only the upper triangle of the corr_matrix, excluding the diagonal,|
//| since each pair otherwise appears twice and a symbol's correlation       |
//| with itself carries no analytical meaning. Every pair at or beyond the   |
//| warning threshold is printed as its own line; every undefined pair is    |
//| counted separately rather than silently ignored. When the corr_matrix has|
//| fewer than two symbols there is nothing to correlate, so that case is    |
//| reported explicitly instead of falling through to an empty best/worst    |
//| search.                                                                  |
//+--------------------------------------------------------------------------+
void CCorrelationSummaryPrinter::Print(const CCorrelationMatrix &corr_matrix,const double warning_threshold) const
  {
   int size = corr_matrix.Size();
   ::Print("Symbol Correlation Summary");
   ::PrintFormat("Symbols monitored: %d", size);

   if(size < 2)
     {
      ::Print("Fewer than two symbols are open, so no pair can be correlated.");
      return;
     }

   int    warning_count   = 0;
   int    undefined_count = 0;
   int    best_row        = -1, best_col = -1;
   int    worst_row       = -1, worst_col = -1;
   double best_value      = 0.0, worst_value = 0.0;
   bool   best_found      = false, worst_found = false;

   for(int row = 0; row < size; row++)
     {
      for(int col = row + 1; col < size; col++)
        {
         if(!corr_matrix.IsDefined(row, col))
           {
            undefined_count++;
            continue;
           }
         double value = corr_matrix.GetValue(row, col);

         if(::MathAbs(value) >= warning_threshold)
           {
            warning_count++;
            ::PrintFormat("WARNING: %s <-> %s   r = %.2f   exceeds the %.2f threshold",
                          corr_matrix.GetSymbol(row), corr_matrix.GetSymbol(col), value, warning_threshold);
           }

         if(!best_found || value > best_value)
           {
            best_found = true;
            best_value = value;
            best_row   = row;
            best_col   = col;
           }
         if(!worst_found || value < worst_value)
           {
            worst_found = true;
            worst_value = value;
            worst_row   = row;
            worst_col   = col;
           }
        }
     }

   ::PrintFormat("Pairs at or beyond the warning threshold: %d", warning_count);
   if(undefined_count > 0)
      ::PrintFormat("Pairs that could not be scored (flat returns over the window): %d", undefined_count);

   if(best_found)
      ::PrintFormat("Highest correlation: %s <-> %s   r = %.2f", corr_matrix.GetSymbol(best_row), corr_matrix.GetSymbol(best_col), best_value);
   if(worst_found)
      ::PrintFormat("Lowest correlation:  %s <-> %s   r = %.2f", corr_matrix.GetSymbol(worst_row), corr_matrix.GetSymbol(worst_col), worst_value);
   if(!best_found)
      ::Print("No pair in the corr_matrix produced a defined correlation.");
  }

#endif // CORRELATIONSUMMARYPRINTER_MQH
//+------------------------------------------------------------------+