//+------------------------------------------------------------------+
//|                                             SessionClassifier.mqh|
//+------------------------------------------------------------------+
#ifndef SESSIONCLASSIFIER_MQH
#define SESSIONCLASSIFIER_MQH

#include "SessionEnums.mqh"

#define SESSION_BOUNDARY_COUNT 4

//+------------------------------------------------------------------+
//| CSessionClassifier                                               |
//| Classifies a UTC datetime into one of the four configured        |
//| trading sessions.                                                |
//+------------------------------------------------------------------+
class CSessionClassifier
  {
private:
   CSessionBoundary      m_boundaries[SESSION_BOUNDARY_COUNT];
   int                   m_boundary_count;

public:
                     CSessionClassifier(void);
                    ~CSessionClassifier(void);

   void                  LoadDefaults(void);
   ENUM_TRADING_SESSION  Classify(datetime close_time_utc) const;
   int                   BoundaryCount(void) const;
   bool                  GetBoundary(int index, CSessionBoundary &boundary_out) const;
  };

//+------------------------------------------------------------------+
//| Constructor: initializes the boundary count to zero until        |
//| LoadDefaults or a custom loader populates the array.             |
//+------------------------------------------------------------------+
CSessionClassifier::CSessionClassifier(void)
  {
   m_boundary_count = 0;
  }

//+------------------------------------------------------------------+
//| Destructor: no dynamic resources to release.                     |
//+------------------------------------------------------------------+
CSessionClassifier::~CSessionClassifier(void)
  {
  }

//+------------------------------------------------------------------+
//| LoadDefaults                                                     |
//| Populates the four standard trading sessions with their          |
//| commonly accepted UTC boundaries. London is inserted before      |
//| New York so the overlapping hours resolve to London.             |
//+------------------------------------------------------------------+
void CSessionClassifier::LoadDefaults(void)
  {
//--- define the Sydney session, which wraps past midnight in UTC
   m_boundaries[0].session_type   = SESSION_SYDNEY;
   m_boundaries[0].name           = "Sydney";
   m_boundaries[0].utc_open_hour  = 22;
   m_boundaries[0].utc_close_hour = 7;
   m_boundaries[0].bar_color      = clrForestGreen;
//--- define the Tokyo session
   m_boundaries[1].session_type   = SESSION_TOKYO;
   m_boundaries[1].name           = "Tokyo";
   m_boundaries[1].utc_open_hour  = 0;
   m_boundaries[1].utc_close_hour = 9;
   m_boundaries[1].bar_color      = clrForestGreen;
//--- define the London session, inserted before New York deliberately
   m_boundaries[2].session_type   = SESSION_LONDON;
   m_boundaries[2].name           = "London";
   m_boundaries[2].utc_open_hour  = 8;
   m_boundaries[2].utc_close_hour = 17;
   m_boundaries[2].bar_color      = clrForestGreen;
//--- define the New York session
   m_boundaries[3].session_type   = SESSION_NEW_YORK;
   m_boundaries[3].name           = "New York";
   m_boundaries[3].utc_open_hour  = 13;
   m_boundaries[3].utc_close_hour = 22;
   m_boundaries[3].bar_color      = clrForestGreen;
//--- record how many boundaries were loaded
   m_boundary_count = SESSION_BOUNDARY_COUNT;
  }

//+------------------------------------------------------------------+
//| Classify                                                         |
//| Returns the trading session that the given UTC datetime falls    |
//| into, checking boundaries in array order and returning the       |
//| first match, or SESSION_UNKNOWN if no boundary matches.          |
//+------------------------------------------------------------------+
ENUM_TRADING_SESSION CSessionClassifier::Classify(datetime close_time_utc) const
  {
//--- extract the UTC hour component from the close time
   MqlDateTime dt;
   ::TimeToStruct(close_time_utc, dt);
   int hour = dt.hour;
//--- compare the hour against each configured boundary in order
   for(int i = 0; i < m_boundary_count; i++)
     {
      int open_hour  = m_boundaries[i].utc_open_hour;
      int close_hour = m_boundaries[i].utc_close_hour;
      //--- same-day boundary: open hour precedes close hour
      if(open_hour < close_hour)
        {
         if(hour >= open_hour && hour < close_hour)
            return(m_boundaries[i].session_type);
        }
      //--- midnight-crossing boundary: open hour follows close hour
      else
        {
         if(hour >= open_hour || hour < close_hour)
            return(m_boundaries[i].session_type);
        }
     }
//--- no configured boundary matched this hour
   return(SESSION_UNKNOWN);
  }

//+------------------------------------------------------------------+
//| BoundaryCount                                                    |
//| Returns the number of boundaries currently loaded.               |
//+------------------------------------------------------------------+
int CSessionClassifier::BoundaryCount(void) const
  {
   return(m_boundary_count);
  }

//+------------------------------------------------------------------+
//| GetBoundary                                                      |
//| Copies the boundary at the given index into boundary_out.        |
//| Returns false if the index is out of range.                      |
//+------------------------------------------------------------------+
bool CSessionClassifier::GetBoundary(int index, CSessionBoundary &boundary_out) const
  {
//--- validates the requested index before copying
   if(index < 0 || index >= m_boundary_count)
      return(false);
   boundary_out = m_boundaries[index];
   return(true);
  }

#endif // SESSIONCLASSIFIER_MQH
//+------------------------------------------------------------------+