//+------------------------------------------------------------------+
//|                                           PriceHistogram.mqh     |
//|                      Session Volume Profile Engine               |
//+------------------------------------------------------------------+
#ifndef PRICE_HISTOGRAM_MQH
#define PRICE_HISTOGRAM_MQH

#include "TickCollector.mqh"

//+------------------------------------------------------------------+
//| Price Histogram Class                                            |
//+------------------------------------------------------------------+
class CPriceHistogram
  {
private:
   double             m_bin_volumes[];
   double             m_session_low;
   double             m_session_high;
   double             m_bin_size;
   int                m_bin_count;
   double             m_total_volume;
   string             m_symbol;

public:
                     CPriceHistogram(void);
                    ~CPriceHistogram(void);
   bool               Build(const string symbol,
                            double bin_size_points,
                            const CTickCollector &collector);
   int                GetBinCount(void)              const;
   double             GetBinVolume(int bin)          const;
   double             GetBinPrice(int bin)           const;
   double             GetSessionLow(void)            const;
   double             GetSessionHigh(void)           const;
   double             GetTotalVolume(void)           const;
   void               Reset(void);

private:
   int                PriceToBin(double price)       const;
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPriceHistogram::CPriceHistogram(void)
   : m_session_low(0.0),
     m_session_high(0.0),
     m_bin_size(0.0),
     m_bin_count(0),
     m_total_volume(0.0)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPriceHistogram::~CPriceHistogram(void)
  {
   ArrayFree(m_bin_volumes);
  }

//+------------------------------------------------------------------+
//| Reset histogram state                                            |
//+------------------------------------------------------------------+
void CPriceHistogram::Reset(void)
  {
   ArrayFree(m_bin_volumes);
   m_session_low   = 0.0;
   m_session_high  = 0.0;
   m_bin_size      = 0.0;
   m_bin_count     = 0;
   m_total_volume  = 0.0;
  }

//+------------------------------------------------------------------+
//| Build histogram from collected ticks                             |
//+------------------------------------------------------------------+
bool CPriceHistogram::Build(const string symbol,
                            double bin_size_points,
                            const CTickCollector &collector)
  {
   int tick_count = collector.GetCount();
   if(tick_count <= 0)
      return(false);

//--- Resolve bin size in price units
   double point     = SymbolInfoDouble(symbol, SYMBOL_POINT);
   m_bin_size       = bin_size_points * point;
   m_symbol         = symbol;

//--- First pass: find session high and low
   m_session_low    = collector.GetPrice(0);
   m_session_high   = collector.GetPrice(0);

   for(int i = 1; i < tick_count; i++)
     {
      double p = collector.GetPrice(i);
      if(p < m_session_low)
         m_session_low  = p;
      if(p > m_session_high)
         m_session_high = p;
     }

//--- Calculate number of price bins
   m_bin_count = (int)MathCeil((m_session_high - m_session_low) / m_bin_size) + 1;

   if(m_bin_count <= 0 || m_bin_count > 10000)
      return(false);

//--- Allocate and zero the bin volume array
   ArrayResize(m_bin_volumes, m_bin_count);
   ArrayInitialize(m_bin_volumes, 0.0);

//--- Second pass: accumulate volume into bins
   m_total_volume = 0.0;

   for(int i = 0; i < tick_count; i++)
     {
      double price  = collector.GetPrice(i);
      long   vol    = collector.GetVolume(i);
      int    bin    = PriceToBin(price);

      if(bin >= 0 && bin < m_bin_count)
        {
         m_bin_volumes[bin] += (double)vol;
         m_total_volume     += (double)vol;
        }
     }

   return(true);
  }

//+------------------------------------------------------------------+
//| Map price to bin index                                           |
//+------------------------------------------------------------------+
int CPriceHistogram::PriceToBin(double price) const
  {
   return((int)MathFloor((price - m_session_low) / m_bin_size));
  }

//+------------------------------------------------------------------+
//| Return accumulated volume for a specific bin                     |
//+------------------------------------------------------------------+
double CPriceHistogram::GetBinVolume(int bin) const
  {
   if(bin < 0 || bin >= m_bin_count)
      return(0.0);
   return(m_bin_volumes[bin]);
  }

//+------------------------------------------------------------------+
//| Return the reference price (lower edge) of a specific bin        |
//+------------------------------------------------------------------+
double CPriceHistogram::GetBinPrice(int bin) const
  {
   return(m_session_low + bin * m_bin_size);
  }

//+------------------------------------------------------------------+
//| Accessor methods                                                 |
//+------------------------------------------------------------------+
int CPriceHistogram::GetBinCount(void) const
  {
   return(m_bin_count);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CPriceHistogram::GetSessionLow(void) const
  {
   return(m_session_low);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CPriceHistogram::GetSessionHigh(void) const
  {
   return(m_session_high);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CPriceHistogram::GetTotalVolume(void) const
  {
   return(m_total_volume);
  }

#endif // PRICE_HISTOGRAM_MQH
//+------------------------------------------------------------------+