//+------------------------------------------------------------------+
//|                                          ProfileRenderer.mqh     |
//|                      Session Volume Profile Engine               |
//+------------------------------------------------------------------+
#ifndef PROFILE_RENDERER_MQH
#define PROFILE_RENDERER_MQH

#include "PriceHistogram.mqh"

//+------------------------------------------------------------------+
//| Profile Renderer Class                                           |
//+------------------------------------------------------------------+
class CProfileRenderer
  {
private:
   string             m_prefix;
   color              m_bar_color;
   color              m_poc_color;
   color              m_vah_color;
   color              m_val_color;
   int                m_bar_width;
   int                m_fixed_bar_count;
   bool               m_use_fixed_width;

public:
                     CProfileRenderer(void);
                    ~CProfileRenderer(void);
   void               Configure(string prefix,
                                color bar_color,
                                color poc_color,
                                color vah_color,
                                color val_color,
                                int bar_width,
                                int fixed_bar_count,
                                bool use_fixed_width);
   void               RemoveSessionObjects(void);
   void               Render(const CPriceHistogram &histogram,
                             double poc_price,
                             double vah_price,
                             double val_price,
                             datetime session_start,
                             datetime session_end);

private:
   void               DrawBar(int bin,
                              double bin_price,
                              double volume,
                              double max_volume,
                              datetime session_end,
                              long bar_span_seconds);
   void               DrawHLine(string name,
                                double price,
                                datetime session_start,
                                datetime session_end,
                                color line_color,
                                int line_style);
   long               ResolveBarSpan(datetime session_start,
                                     datetime session_end);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProfileRenderer::CProfileRenderer(void)
   : m_prefix("SVP_"),
     m_bar_color(clrSteelBlue),
     m_poc_color(clrYellow),
     m_vah_color(clrLimeGreen),
     m_val_color(clrRed),
     m_bar_width(2),
     m_fixed_bar_count(20),
     m_use_fixed_width(true)
  {
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CProfileRenderer::~CProfileRenderer(void)
  {
  }

//+------------------------------------------------------------------+
//| Configure renderer parameters                                    |
//+------------------------------------------------------------------+
void CProfileRenderer::Configure(string prefix,
                                 color bar_color,
                                 color poc_color,
                                 color vah_color,
                                 color val_color,
                                 int bar_width,
                                 int fixed_bar_count,
                                 bool use_fixed_width)
  {
   m_prefix          = prefix;
   m_bar_color       = bar_color;
   m_poc_color       = poc_color;
   m_vah_color       = vah_color;
   m_val_color       = val_color;
   m_bar_width       = bar_width;
   m_fixed_bar_count = fixed_bar_count;
   m_use_fixed_width = use_fixed_width;
  }

//+------------------------------------------------------------------+
//| Remove all chart objects belonging to this session               |
//+------------------------------------------------------------------+
void CProfileRenderer::RemoveSessionObjects(void)
  {
   long chart_id = ChartID();
   int  total    = ObjectsTotal(chart_id, 0, -1);

//--- Iterate in reverse to safely delete during traversal
   for(int i = total - 1; i >= 0; i--)
     {
      string name = ObjectName(chart_id, i, 0, -1);
      if(StringFind(name, m_prefix) == 0)
         ObjectDelete(chart_id, name);
     }
  }

//+------------------------------------------------------------------+
//| Resolve the time span in seconds for the maximum histogram bar   |
//+------------------------------------------------------------------+
long CProfileRenderer::ResolveBarSpan(datetime session_start,
                                      datetime session_end)
  {
   if(m_use_fixed_width)
     {
      //--- Fixed mode: POC bar spans exactly m_fixed_bar_count chart bars
      long period_seconds = (long)PeriodSeconds();
      return(period_seconds * m_fixed_bar_count);
     }

//--- Proportional mode: POC bar spans the full session duration
   return((long)(session_end - session_start));
  }

//+------------------------------------------------------------------+
//| Render full volume profile                                       |
//+------------------------------------------------------------------+
void CProfileRenderer::Render(const CPriceHistogram &histogram,
                              double poc_price,
                              double vah_price,
                              double val_price,
                              datetime session_start,
                              datetime session_end)
  {
   int    bin_count  = histogram.GetBinCount();
   double max_volume = 0.0;

//--- Find maximum bin volume for bar scaling
   for(int i = 0; i < bin_count; i++)
     {
      double v = histogram.GetBinVolume(i);
      if(v > max_volume)
         max_volume = v;
     }

   if(max_volume <= 0.0)
      return;

//--- Resolve the time span for the longest bar
   long bar_span_seconds = ResolveBarSpan(session_start, session_end);

//--- Draw histogram bars
   for(int i = 0; i < bin_count; i++)
     {
      double bin_price  = histogram.GetBinPrice(i);
      double bin_volume = histogram.GetBinVolume(i);

      if(bin_volume > 0.0)
         DrawBar(i, bin_price, bin_volume, max_volume,
                 session_end, bar_span_seconds);
     }

//--- Draw POC line
   DrawHLine(m_prefix + "POC",
             poc_price,
             session_start,
             session_end,
             m_poc_color,
             STYLE_SOLID);

//--- Draw VAH line
   DrawHLine(m_prefix + "VAH",
             vah_price,
             session_start,
             session_end,
             m_vah_color,
             STYLE_DASH);

//--- Draw VAL line
   DrawHLine(m_prefix + "VAL",
             val_price,
             session_start,
             session_end,
             m_val_color,
             STYLE_DASH);

   ChartRedraw();
  }

//+------------------------------------------------------------------+
//| Draw a single histogram bar anchored to session end              |
//+------------------------------------------------------------------+
void CProfileRenderer::DrawBar(int bin,
                               double bin_price,
                               double volume,
                               double max_volume,
                               datetime session_end,
                               long bar_span_seconds)
  {
//--- Scale bar length proportionally to the maximum bin volume
   double   ratio     = volume / max_volume;
   long     secs      = (long)MathRound((double)bar_span_seconds * ratio);
   datetime bar_start = session_end - (datetime)secs;
   datetime bar_end   = session_end;

   string name = m_prefix + "BAR_" + IntegerToString(bin);

   if(ObjectFind(ChartID(), name) < 0)
      ObjectCreate(ChartID(), name, OBJ_TREND, 0,
                   bar_start, bin_price,
                   bar_end,   bin_price);
   else
     {
      //--- Update anchor times if object already exists
      ObjectSetInteger(ChartID(), name, OBJPROP_TIME,  0, bar_start);
      ObjectSetInteger(ChartID(), name, OBJPROP_TIME,  1, bar_end);
      ObjectSetDouble(ChartID(),  name, OBJPROP_PRICE, 0, bin_price);
      ObjectSetDouble(ChartID(),  name, OBJPROP_PRICE, 1, bin_price);
     }

   ObjectSetInteger(ChartID(), name, OBJPROP_COLOR,      m_bar_color);
   ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH,      m_bar_width);
   ObjectSetInteger(ChartID(), name, OBJPROP_STYLE,      STYLE_SOLID);
   ObjectSetInteger(ChartID(), name, OBJPROP_RAY,        false);
   ObjectSetInteger(ChartID(), name, OBJPROP_SELECTABLE, false);
  }

//+------------------------------------------------------------------+
//| Draw a horizontal key level line extending rightward             |
//+------------------------------------------------------------------+
void CProfileRenderer::DrawHLine(string name,
                                 double price,
                                 datetime session_start,
                                 datetime session_end,
                                 color line_color,
                                 int line_style)
  {
   if(ObjectFind(ChartID(), name) < 0)
      ObjectCreate(ChartID(), name, OBJ_TREND, 0,
                   session_start, price,
                   session_end,   price);

   ObjectSetInteger(ChartID(), name, OBJPROP_COLOR,      line_color);
   ObjectSetInteger(ChartID(), name, OBJPROP_WIDTH,      2);
   ObjectSetInteger(ChartID(), name, OBJPROP_STYLE,      line_style);
   ObjectSetInteger(ChartID(), name, OBJPROP_RAY_RIGHT,  true);
   ObjectSetInteger(ChartID(), name, OBJPROP_RAY_LEFT,   false);
   ObjectSetInteger(ChartID(), name, OBJPROP_SELECTABLE, false);
  }

#endif // PROFILE_RENDERER_MQH
//+------------------------------------------------------------------+