//+------------------------------------------------------------------+
//|                                                  CMicroPanel.mqh |
//|                                               Patrick M. Njoroge |
//|                  https://www.mql5.com/en/users/patricknjoroge743 |
//+------------------------------------------------------------------+
//|  Control / readout panel for the microstructure-feature viewer.  |
//|                                                                  |
//|  Renders, in the indicator subwindow:                            |
//|    * a title + the rolling window / normalize state              |
//|    * one toggle button per feature (text in the plot's color),   |
//|      whose pressed state mirrors that plot's visibility, with    |
//|      the latest raw value right-aligned beside it                |
//|    * a Normalize toggle and a Refresh button                     |
//|                                                                  |
//|  The panel owns no logic: the host indicator reads the button    |
//|  names (FeatureBtnName / NormalizeBtnName / RefreshBtnName) in   |
//|  OnChartEvent, flips its own state, then calls Update().         |
//+------------------------------------------------------------------+
#ifndef __CMICRO_PANEL_MQH__
#define __CMICRO_PANEL_MQH__

#define MICRO_NFEAT 7        // Roll, RollImpact, CSSpread, CSSigma, Kyle, Amihud, Hasbrouck

//--- guard the theme enum so it can coexist with other panel headers
#ifndef ENUM_PANEL_THEME_DEFINED
#define ENUM_PANEL_THEME_DEFINED
enum ENUM_PANEL_THEME
  {
   PANEL_DARK  = 0,   // Dark
   PANEL_LIGHT = 1    // Light
  };
#endif

//+------------------------------------------------------------------+
//| CMicroPanel                                                      |
//+------------------------------------------------------------------+
class CMicroPanel
  {
private:
   long              m_chart;
   int               m_subwin;
   string            m_prefix;
   int               m_panel_w;
   int               m_panel_h;
   int               m_left;
   int               m_top;
   ENUM_PANEL_THEME  m_theme;

   color             m_feat_col[MICRO_NFEAT];
   string            m_feat_name[MICRO_NFEAT];

   //--- theme colors (resolved in Create)
   color             m_bg, m_border, m_title, m_label, m_value;
   color             m_btn_bg, m_btn_off;

   string            ObjName(const string s) const { return(m_prefix + s); }
   void              MakeLabel(const string name, const int x, const int y,
                               const string text, const color clr,
                               const int sz, const bool bold);
   void              MakeButton(const string name, const int x, const int y,
                                const int w, const int h, const string text,
                                const color txt, const color bg);
   void              DeleteAll(void);

public:
                     CMicroPanel(void);
                    ~CMicroPanel(void);

   bool              Create(const long chart_id, const int subwin,
                            const ENUM_PANEL_THEME theme,
                            const color &cols[], const string &names[]);
   void              Update(const double &latest[], const bool &visible[],
                            const bool normalize, const int window);
   void              Destroy(void);

   string            FeatureBtnName(const int i) const { return(ObjName("_fbtn" + IntegerToString(i))); }
   string            NormalizeBtnName(void)      const { return(ObjName("_norm")); }
   string            RefreshBtnName(void)        const { return(ObjName("_refresh")); }

   //--- format a feature value compactly (scientific for tiny magnitudes)
   static string     FmtValue(const double v);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMicroPanel::CMicroPanel(void)
  {
   m_chart   = 0;
   m_subwin  = 0;
   m_prefix  = "MSV_";
   m_panel_w = 300;
   m_left    = 8;
   m_top     = 60;          // below the host's toggle button row
   m_theme   = PANEL_DARK;
   m_panel_h = 24 + MICRO_NFEAT * 22 + 24 + 26 + 12;   // title + rows + norm + refresh
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CMicroPanel::~CMicroPanel(void) { Destroy(); }

//+------------------------------------------------------------------+
//| FmtValue — compact, magnitude-aware                              |
//+------------------------------------------------------------------+
string CMicroPanel::FmtValue(const double v)
  {
   if(!MathIsValidNumber(v))
      return("n/a");
   double a = MathAbs(v);
   if(a == 0.0)
      return("0");
   if(a < 1.0e-4 || a >= 1.0e6)
      return(StringFormat("%.3e", v));
   if(a < 1.0)
      return(StringFormat("%.6f", v));
   return(StringFormat("%.4f", v));
  }

//+------------------------------------------------------------------+
//| MakeLabel                                                        |
//+------------------------------------------------------------------+
void CMicroPanel::MakeLabel(const string name, const int x, const int y,
                            const string text, const color clr,
                            const int sz, const bool bold)
  {
   string full = ObjName(name);
   if(ObjectFind(m_chart, full) < 0)
     {
      ObjectCreate(m_chart, full, OBJ_LABEL, m_subwin, 0, 0);
      ObjectSetInteger(m_chart, full, OBJPROP_CORNER,     CORNER_LEFT_UPPER);
      ObjectSetInteger(m_chart, full, OBJPROP_ANCHOR,     ANCHOR_LEFT_UPPER);
      ObjectSetInteger(m_chart, full, OBJPROP_BACK,       false);
      ObjectSetInteger(m_chart, full, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(m_chart, full, OBJPROP_ZORDER,     1);
     }
   ObjectSetInteger(m_chart, full, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(m_chart, full, OBJPROP_YDISTANCE, y);
   ObjectSetString(m_chart, full, OBJPROP_TEXT,      text);
   ObjectSetInteger(m_chart, full, OBJPROP_COLOR,     clr);
   ObjectSetInteger(m_chart, full, OBJPROP_FONTSIZE,  sz);
   ObjectSetString(m_chart, full, OBJPROP_FONT, bold ? "Courier New Bold" : "Courier New");
  }

//+------------------------------------------------------------------+
//| MakeButton                                                       |
//+------------------------------------------------------------------+
void CMicroPanel::MakeButton(const string name, const int x, const int y,
                             const int w, const int h, const string text,
                             const color txt, const color bg)
  {
   string full = ObjName(name);
   if(ObjectFind(m_chart, full) < 0)
      ObjectCreate(m_chart, full, OBJ_BUTTON, m_subwin, 0, 0);
   ObjectSetInteger(m_chart, full, OBJPROP_CORNER,       CORNER_LEFT_UPPER);
   ObjectSetInteger(m_chart, full, OBJPROP_XDISTANCE,    x);
   ObjectSetInteger(m_chart, full, OBJPROP_YDISTANCE,    y);
   ObjectSetInteger(m_chart, full, OBJPROP_XSIZE,        w);
   ObjectSetInteger(m_chart, full, OBJPROP_YSIZE,        h);
   ObjectSetString(m_chart, full, OBJPROP_TEXT,          text);
   ObjectSetInteger(m_chart, full, OBJPROP_COLOR,        txt);
   ObjectSetInteger(m_chart, full, OBJPROP_BGCOLOR,      bg);
   ObjectSetInteger(m_chart, full, OBJPROP_BORDER_COLOR, m_border);
   ObjectSetString(m_chart, full, OBJPROP_FONT,          "Courier New");
   ObjectSetInteger(m_chart, full, OBJPROP_FONTSIZE,     7);
   ObjectSetInteger(m_chart, full, OBJPROP_SELECTABLE,   false);
   ObjectSetInteger(m_chart, full, OBJPROP_ZORDER,       2);
  }

//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CMicroPanel::Create(const long chart_id, const int subwin,
                         const ENUM_PANEL_THEME theme,
                         const color &cols[], const string &names[])
  {
   m_chart  = chart_id;
   m_subwin = subwin;
   m_theme  = theme;

   for(int i = 0; i < MICRO_NFEAT; i++)
     {
      m_feat_col[i]  = (i < ArraySize(cols))  ? cols[i]  : clrWhite;
      m_feat_name[i] = (i < ArraySize(names)) ? names[i] : ("F" + IntegerToString(i));
     }

   if(m_theme == PANEL_DARK)
     {
      m_bg     = C'13,17,23';
      m_border = C'48,54,61';
      m_title  = C'88,166,255';
      m_label  = C'139,148,158';
      m_value  = C'230,237,243';
      m_btn_bg = C'22,27,34';
      m_btn_off= C'72,79,88';
     }
   else
     {
      //--- Light theme: all values target WCAG AA contrast (>=4.5:1) at 7pt
      //
      //   Role         Old value          New value        Contrast on bg
      //   m_bg         C'255,255,255'     C'248,250,252'   (panel bg)
      //   m_border     C'208,215,222'     C'140,152,168'   boundary definition
      //   m_title      C'9,105,218'       C'3,78,162'      ~9:1 on m_bg
      //   m_label      C'87,96,106'       C'52,62,78'      ~12:1 on m_bg
      //   m_value      C'31,35,40'        C'20,25,32'      ~17:1 on m_bg
      //   m_btn_bg     C'246,248,250'     C'213,223,235'   clearly != m_bg
      //   m_btn_off    C'175,184,193'     C'85,98,114'     ~4.8:1 on m_btn_bg
      //
      //   Primary failures fixed:
      //     (1) m_btn_bg was 9 units from m_bg — buttons were invisible.
      //     (2) m_btn_off on old m_btn_bg was ~1.9:1 — unreadable at 7pt.
      //
      m_bg     = C'248,250,252';
      m_border = C'140,152,168';
      m_title  = C'3,78,162';
      m_label  = C'52,62,78';
      m_value  = C'20,25,32';
      m_btn_bg = C'213,223,235';
      m_btn_off= C'85,98,114';
     }

//--- background
   string bg = ObjName("_bg");
   if(!ObjectCreate(m_chart, bg, OBJ_RECTANGLE_LABEL, m_subwin, 0, 0))
     {
      PrintFormat("CMicroPanel::Create: ObjectCreate bg failed err=%d", GetLastError());
      return(false);
     }
   ObjectSetInteger(m_chart, bg, OBJPROP_CORNER,       CORNER_LEFT_UPPER);
   ObjectSetInteger(m_chart, bg, OBJPROP_XDISTANCE,    m_left);
   ObjectSetInteger(m_chart, bg, OBJPROP_YDISTANCE,    m_top);
   ObjectSetInteger(m_chart, bg, OBJPROP_XSIZE,        m_panel_w);
   ObjectSetInteger(m_chart, bg, OBJPROP_YSIZE,        m_panel_h);
   ObjectSetInteger(m_chart, bg, OBJPROP_BGCOLOR,      m_bg);
   ObjectSetInteger(m_chart, bg, OBJPROP_BORDER_COLOR, m_border);
   ObjectSetInteger(m_chart, bg, OBJPROP_BORDER_TYPE,  BORDER_FLAT);
   ObjectSetInteger(m_chart, bg, OBJPROP_BACK,         true);
   ObjectSetInteger(m_chart, bg, OBJPROP_SELECTABLE,   false);
   ObjectSetInteger(m_chart, bg, OBJPROP_ZORDER,       0);

//--- title
   MakeLabel("_title", m_left + 8, m_top + 6, "Microstructure", m_title, 8, true);

//--- per-feature toggle buttons + value labels
   int btn_w = 150;
   int row_y = m_top + 24;
   int val_x = m_left + m_panel_w - 96;
   for(int i = 0; i < MICRO_NFEAT; i++)
     {
      int y = row_y + i * 22;
      MakeButton("_fbtn" + IntegerToString(i), m_left + 8, y, btn_w, 18,
                 m_feat_name[i], m_feat_col[i], m_btn_bg);
      MakeLabel("_fval" + IntegerToString(i), val_x, y + 3, "—", m_value, 7, false);
     }

//--- normalize toggle + refresh
   int ny = row_y + MICRO_NFEAT * 22 + 2;
   MakeButton("_norm", m_left + 8, ny, m_panel_w - 16, 20, "Normalize (z-score): ON",
              m_title, m_btn_bg);
   MakeButton("_refresh", m_left + 8, ny + 24, m_panel_w - 16, 20, "Recompute",
              m_value, m_btn_bg);

   ChartRedraw(m_chart);
   return(true);
  }

//+------------------------------------------------------------------+
//| Update — refresh values, button states, normalize/window text    |
//+------------------------------------------------------------------+
void CMicroPanel::Update(const double &latest[], const bool &visible[],
                         const bool normalize, const int window)
  {
   for(int i = 0; i < MICRO_NFEAT; i++)
     {
      bool   on  = (i < ArraySize(visible)) ? visible[i] : false;
      double val = (i < ArraySize(latest))  ? latest[i]  : EMPTY_VALUE;

      string fbtn = FeatureBtnName(i);
      ObjectSetInteger(m_chart, fbtn, OBJPROP_STATE, on);
      ObjectSetInteger(m_chart, fbtn, OBJPROP_COLOR, on ? m_feat_col[i] : m_btn_off);

      ObjectSetString(m_chart, ObjName("_fval" + IntegerToString(i)),
                      OBJPROP_TEXT, FmtValue(val));
      ObjectSetInteger(m_chart, ObjName("_fval" + IntegerToString(i)),
                       OBJPROP_COLOR, on ? m_value : m_btn_off);
     }

   ObjectSetInteger(m_chart, NormalizeBtnName(), OBJPROP_STATE, normalize);
   ObjectSetString(m_chart, NormalizeBtnName(), OBJPROP_TEXT,
                   normalize ? "Normalize (z-score): ON" : "Normalize (z-score): OFF");

   ObjectSetString(m_chart, ObjName("_title"), OBJPROP_TEXT,
                   StringFormat("Microstructure  (W=%d)", window));

   ObjectSetInteger(m_chart, RefreshBtnName(), OBJPROP_STATE, false);
   ChartRedraw(m_chart);
  }

//+------------------------------------------------------------------+
//| DeleteAll                                                        |
//+------------------------------------------------------------------+
void CMicroPanel::DeleteAll(void)
  {
   ObjectDelete(m_chart, ObjName("_bg"));
   ObjectDelete(m_chart, ObjName("_title"));
   ObjectDelete(m_chart, NormalizeBtnName());
   ObjectDelete(m_chart, RefreshBtnName());
   for(int i = 0; i < MICRO_NFEAT; i++)
     {
      ObjectDelete(m_chart, FeatureBtnName(i));
      ObjectDelete(m_chart, ObjName("_fval" + IntegerToString(i)));
     }
  }

//+------------------------------------------------------------------+
//| Destroy                                                          |
//+------------------------------------------------------------------+
void CMicroPanel::Destroy(void) { DeleteAll(); ChartRedraw(m_chart); }

#endif
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
