//+------------------------------------------------------------------+
//|        RCM_Matrix_Pro.mq4                                        |
//|                                                                  |
//|       (Multi-Symbol Macro Heatmap Scanner)                       |
//+------------------------------------------------------------------+
#property copyright "Expert Quantitative Architect"
#property version   "6.66"
#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 clrNONE

#define PREFIX "RCM_MAT_"

//--- Enums for Clean Menus
enum ENUM_SYM_MODE {
    MODE_AUTO = 0,     // Auto: Scan Market Watch
    MODE_MANUAL = 1    // Manual: Use Custom String
};
enum ENUM_CALC_MODE {
    MODE_REPAINTING = 0,      // Repainting Mode (Live Tick)
    MODE_NON_REPAINTING = 1   // Non-Repainting Mode (Closed Bar)
};

//==================================================================//
// [1] PAIR SELECTION SETTINGS
//==================================================================//
input string            _p1_           = "=== PAIR SELECTION ===";      
input ENUM_SYM_MODE     InpSymMode     = MODE_MANUAL;                   
input string            InpManualSym   = "EURUSD,GBPUSD,AUDCAD,USDJPY,GBPAUD,NZDUSD,USDCAD,CHFJPY"; 
input string            InpPrefix      = "";                            
input string            InpSuffix      = "m";                           

//==================================================================//
// [2] GRID GEOMETRY & LAYOUT
//==================================================================//
input string            _p2_           = "=== GRID GEOMETRY ===";       
input ENUM_BASE_CORNER  InpCorner      = CORNER_RIGHT_UPPER;            
input int               InpOffsetX     = 20;                            
input int               InpOffsetY     = 30;                            
input int               InpMaxPerCol   = 8;                             

//==================================================================//
// [3] UI STYLING & COLORS
//==================================================================//
input string            _p3_           = "=== UI STYLING ===";          
input color             Clr_Bg         = C'15,18,22';                   
input color             Clr_Header     = C'22,26,32';                   
input color             Clr_Border     = C'45,50,60';                   
input color             Clr_Text       = clrSnow;                       
input color             Clr_Bull       = C'70,180,150';                 
input color             Clr_Bear       = C'240,80,100';                 
input color             Clr_Neutral    = clrGray;                       

//==================================================================//
// [4] PERFORMANCE & ENGINE
//==================================================================//
input string            _p4_           = "=== ENGINE SETTINGS ===";     
input int               InpRefresh     = 3;                             
input string            InpRCMName     = "Relative Currency Momentum";  
input ENUM_CALC_MODE    InpCalcMode    = MODE_REPAINTING;               
input int               InpMaxBars     = 1000;                          

//--- Global Variables
string   Symbols[];
int      SymbolCount = 0;
datetime last_update = 0;
bool     g_Minimized = false;

int      g_PanelX = 0;
int      g_PanelY = 0;
int      g_LastChartW = 0;
int      g_LastChartH = 0;

//--- Aesthetically Corrected Geometry Constants
#define ROW_HEIGHT 36    
#define HDR_HEIGHT 44    
#define COL_BIAS   65    
#define COL_TF     45    
#define COL_NAME   45    
#define BLOCK_WIDTH (COL_BIAS + (COL_TF*4) + COL_NAME) // Total = 290px per block

ENUM_TIMEFRAMES TFs[4] = {PERIOD_D1, PERIOD_H4, PERIOD_H1, PERIOD_M15};
string          TFNms[4]= {"D1", "H4", "H1", "M15"};

double DummyBuffer[];

//==================================================================//
// Initialization
//==================================================================//
int OnInit()
{
    SetIndexBuffer(0, DummyBuffer); SetIndexStyle(0, DRAW_NONE);
    IndicatorShortName("RCM Matrix Pro v6");

    BuildSymbolList();

    string memKey = PREFIX + "MinState_" + Symbol();
    if(GlobalVariableCheck(memKey)) g_Minimized = (GlobalVariableGet(memKey) == 1.0);

    RebuildUI();

    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, PREFIX);
}

//==================================================================//
// Symbol Management
//==================================================================//
void BuildSymbolList()
{
    string temp_syms[];
    int temp_count = 0;

    if(InpSymMode == MODE_AUTO) 
    {
        int mw_total = SymbolsTotal(true);
        temp_count = MathMin(mw_total, 30);
        ArrayResize(temp_syms, temp_count);
        for(int i = 0; i < temp_count; i++) temp_syms[i] = SymbolName(i, true);
    } 
    else 
    {
        string sep = ",";
        ushort u_sep = StringGetCharacter(sep, 0);
        temp_count = StringSplit(InpManualSym, u_sep, temp_syms);
        temp_count = MathMin(temp_count, 30);
        for(int i = 0; i < temp_count; i++) {
            StringTrimLeft(temp_syms[i]); StringTrimRight(temp_syms[i]);
            temp_syms[i] = InpPrefix + temp_syms[i] + InpSuffix;
        }
    }

    SymbolCount = temp_count + 1;
    ArrayResize(Symbols, SymbolCount);
    Symbols[0] = Symbol(); // Force current chart symbol to Index 0
    
    int idx = 1;
    for(int i = 0; i < temp_count; i++) {
        if(temp_syms[i] != Symbol()) {
            Symbols[idx] = temp_syms[i];
            idx++;
        }
    }
    SymbolCount = idx; 
    ArrayResize(Symbols, SymbolCount); 
}

//==================================================================//
// Absolute Positioning Engine 
//==================================================================//
void ComputePanelOrigin()
{
    g_LastChartW = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
    g_LastChartH = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);

    int total_blocks = (int)MathCeil((double)SymbolCount / InpMaxPerCol);
    int total_w = total_blocks * BLOCK_WIDTH;
    int items_in_col = MathMin(SymbolCount, InpMaxPerCol);
    int total_h = HDR_HEIGHT + (items_in_col * ROW_HEIGHT);

    if(g_Minimized) { total_w = BLOCK_WIDTH; total_h = HDR_HEIGHT + ROW_HEIGHT; }

    switch(InpCorner) {
        case CORNER_LEFT_UPPER:  g_PanelX = InpOffsetX; g_PanelY = InpOffsetY; break;
        case CORNER_RIGHT_UPPER: g_PanelX = g_LastChartW - InpOffsetX - total_w; g_PanelY = InpOffsetY; break;
        case CORNER_LEFT_LOWER:  g_PanelX = InpOffsetX; g_PanelY = g_LastChartH - InpOffsetY - total_h; break;
        case CORNER_RIGHT_LOWER: g_PanelX = g_LastChartW - InpOffsetX - total_w; g_PanelY = g_LastChartH - InpOffsetY - total_h; break;
    }
}

//==================================================================//
// UI Object Factories
//==================================================================//
void MakeRect(string name, int x, int y, int w, int h, color bg, color border) {
    if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
    ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
    ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
    ObjectSetInteger(0, name, OBJPROP_COLOR, border);
    ObjectSetInteger(0, name, OBJPROP_BACK, false);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}

void MakeLabel(string name, int x, int y, string text, color clr, int fontSize, int anchor) {
    if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
    ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}

void MakeButton(string name, int x, int y, int w, int h, string text) {
    if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
    ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
    ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_BGCOLOR, Clr_Header);
    ObjectSetInteger(0, name, OBJPROP_COLOR, Clr_Text);
    ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, Clr_Border);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}

//==================================================================//
// The Spreadsheet Grid Builder
//==================================================================//
void BuildDashboard()
{
    int total_blocks = (int)MathCeil((double)SymbolCount / InpMaxPerCol);
    int items_in_col = MathMin(SymbolCount, InpMaxPerCol);
    int total_w = total_blocks * BLOCK_WIDTH;
    int total_h = HDR_HEIGHT + (items_in_col * ROW_HEIGHT);

    // Main Background
    MakeRect(PREFIX+"BG", g_PanelX, g_PanelY, total_w, total_h, Clr_Bg, Clr_Border);
    MakeRect(PREFIX+"HDR_BG", g_PanelX, g_PanelY, total_w, HDR_HEIGHT, Clr_Header, Clr_Border);

    // Master Minimize Button (Top Right of the entire matrix)
    int minBtnX = g_PanelX + total_w - 25;
    MakeButton(PREFIX+"BTN_MIN", minBtnX, g_PanelY + 12, 18, 18, g_Minimized ? "+" : "-");

    // Render Blocks & Spreadsheet Cells
    for(int b = 0; b < total_blocks; b++) 
    {
        int bX = g_PanelX + (b * BLOCK_WIDTH);
        string bp = PREFIX + "B" + IntegerToString(b) + "_"; 

        MakeLabel(bp+"TITLE", bX + (BLOCK_WIDTH/2), g_PanelY + 6, "MTF Strength Panel", Clr_Text, 8, ANCHOR_UPPER);
        
        for(int tf=0; tf<4; tf++) {
            MakeLabel(bp+"H_"+IntegerToString(tf), bX + COL_BIAS + (tf*COL_TF) + (COL_TF/2), g_PanelY + 24, TFNms[tf], Clr_Text, 8, ANCHOR_UPPER);
        }

        MakeRect(bp+"HDivH", bX, g_PanelY + HDR_HEIGHT - 1, BLOCK_WIDTH, 1, Clr_Border, Clr_Border);

        for(int v=0; v<5; v++) MakeRect(bp+"VDivH_"+IntegerToString(v), bX + COL_BIAS + (v*COL_TF), g_PanelY + 22, 1, HDR_HEIGHT - 22, Clr_Border, Clr_Border);

        for(int r = 0; r < InpMaxPerCol; r++) 
        {
            int sym_idx = (b * InpMaxPerCol) + r;
            if(sym_idx >= SymbolCount) break;

            int rY = g_PanelY + HDR_HEIGHT + (r * ROW_HEIGHT);
            string rp = PREFIX + "R" + IntegerToString(sym_idx) + "_"; 
            
            MakeRect(rp+"HDiv", bX, rY + ROW_HEIGHT - 1, BLOCK_WIDTH, 1, Clr_Border, Clr_Border);
            for(int v=0; v<5; v++) MakeRect(rp+"VDiv_"+IntegerToString(v), bX + COL_BIAS + (v*COL_TF), rY, 1, ROW_HEIGHT, Clr_Border, Clr_Border);

            MakeLabel(rp+"BIAS", bX + (COL_BIAS/2), rY + (ROW_HEIGHT/2), "LOADING", Clr_Neutral, 8, ANCHOR_CENTER);
            
            for(int tf=0; tf<4; tf++) {
                int cellCenter = bX + COL_BIAS + (tf*COL_TF) + (COL_TF/2);
                MakeLabel(rp+"B_"+IntegerToString(tf), cellCenter, rY + 4, "----", Clr_Neutral, 8, ANCHOR_UPPER);
                MakeLabel(rp+"Q_"+IntegerToString(tf), cellCenter, rY + 18, "----", Clr_Neutral, 8, ANCHOR_UPPER);
            }

            int p_len = StringLen(InpPrefix);
            string b_name = StringSubstr(Symbols[sym_idx], p_len, 3);
            string q_name = StringSubstr(Symbols[sym_idx], p_len+3, 3);
            
            int nameCenter = bX + COL_BIAS + (4*COL_TF) + (COL_NAME/2);
            MakeLabel(rp+"N_B", nameCenter, rY + 4, b_name, Clr_Text, 8, ANCHOR_UPPER);
            MakeLabel(rp+"N_Q", nameCenter, rY + 18, q_name, Clr_Text, 8, ANCHOR_UPPER);
        }
    }
}

//==================================================================//
// Dual-State Visibility Engine
//==================================================================//
void ApplyMinMaxState()
{
    int total_blocks = (int)MathCeil((double)SymbolCount / InpMaxPerCol);
    int total_w = total_blocks * BLOCK_WIDTH;
    int items_in_col = MathMin(SymbolCount, InpMaxPerCol);
    int total_h = HDR_HEIGHT + (items_in_col * ROW_HEIGHT);

    if(g_Minimized) {
        total_w = BLOCK_WIDTH; 
        total_h = HDR_HEIGHT + ROW_HEIGHT; 
    }

    ObjectSetInteger(0, PREFIX+"BG", OBJPROP_XSIZE, total_w);
    ObjectSetInteger(0, PREFIX+"BG", OBJPROP_YSIZE, total_h);
    ObjectSetInteger(0, PREFIX+"HDR_BG", OBJPROP_XSIZE, total_w);
    
    int minBtnX = g_PanelX + total_w - 25;
    ObjectSetInteger(0, PREFIX+"BTN_MIN", OBJPROP_XDISTANCE, minBtnX);
    ObjectSetString(0, PREFIX+"BTN_MIN", OBJPROP_TEXT, g_Minimized ? "+" : "-");

    int total_objs = ObjectsTotal(0, -1, -1);
    for(int i = 0; i < total_objs; i++) 
    {
        string nm = ObjectName(0, i, -1, -1);
        if(StringFind(nm, PREFIX) != 0) continue;

        bool is_visible = true;

        if(g_Minimized) {
            // Hide everything except the first Block and the first Row
            if(StringFind(nm, PREFIX+"B0_") == -1 && 
               StringFind(nm, PREFIX+"R0_") == -1 && 
               nm != PREFIX+"BG" && 
               nm != PREFIX+"HDR_BG" && 
               nm != PREFIX+"BTN_MIN") 
            {
                is_visible = false;
            }
        }
        ObjectSetInteger(0, nm, OBJPROP_TIMEFRAMES, is_visible ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS);
    }
}

void RebuildUI()
{
    ObjectsDeleteAll(0, PREFIX);
    ComputePanelOrigin();
    BuildDashboard();
    ApplyMinMaxState();
    last_update = 0; // Force instant data refresh
}

//==================================================================//
// Interactive Event Listener
//==================================================================//
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // Handle Minimize/Maximize Grid Toggle
    if(id == CHARTEVENT_OBJECT_CLICK && sparam == PREFIX + "BTN_MIN") 
    {
        int oldX = g_PanelX;
        int oldY = g_PanelY;

        g_Minimized = !g_Minimized;
        GlobalVariableSet(PREFIX + "MinState_" + Symbol(), g_Minimized ? 1.0 : 0.0);
        
        ComputePanelOrigin(); 
        
        // Calculate the exact mathematical slide distance required
        int dX = g_PanelX - oldX;
        int dY = g_PanelY - oldY;

        // Slide every object instantly across the screen to new coordinates
        int total_objs = ObjectsTotal(0, -1, -1);
        for(int i = 0; i < total_objs; i++) {
            string nm = ObjectName(0, i, -1, -1);
            if(StringFind(nm, PREFIX) == 0) {
                int currX = (int)ObjectGetInteger(0, nm, OBJPROP_XDISTANCE);
                int currY = (int)ObjectGetInteger(0, nm, OBJPROP_YDISTANCE);
                ObjectSetInteger(0, nm, OBJPROP_XDISTANCE, currX + dX);
                ObjectSetInteger(0, nm, OBJPROP_YDISTANCE, currY + dY);
            }
        }

        ApplyMinMaxState();
        ObjectSetInteger(0, sparam, OBJPROP_STATE, false); // Un-press button
        ChartRedraw();
    }
    
    // Screen Resize Listener
    if(id == CHARTEVENT_CHART_CHANGE)
    {
        int newW = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
        int newH = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
        if(newW != g_LastChartW || newH != g_LastChartH) {
            RebuildUI();
            ChartRedraw();
        }
    }
}

//==================================================================//
// The Stealth RCM Bridge
//==================================================================//
double GetRCMData(string sym, int timeframe, int buffer_index)
{
    int shift = (InpCalcMode == MODE_REPAINTING) ? 0 : 1;
    return iCustom(sym, timeframe, InpRCMName,
        "=== DISPLAY SETTINGS ===", 1, 2, C'70,180,150', C'240,80,100', C'70,180,150', C'240,80,100', 20.0, 50.0,
        "=== MOMENTUM SCORE SETTINGS ===", false, "Lucida Console", 14, 80, 15, 12, 32,
        "=== FIBO TARGET SETTINGS ===", false, false, false, clrLime, clrRed,
        "=== PERFORMANCE SETTINGS ===", InpMaxBars, 
        "=== LINE CALCULATION MODE ===", InpCalcMode, 
        "=== ALERT SETTINGS ===", 0, false, false, 20.0, false, 50.0, false, false, false,
        "=== MTF PANEL SETTINGS ===", false, 
        buffer_index, shift);
}

//==================================================================//
// Data Update Engine
//==================================================================//
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
                const double &open[], const double &high[], const double &low[], const double &close[],
                const long &tick_volume[], const long &volume[], const int &spread[])
{
    if(TimeCurrent() - last_update < InpRefresh) return(rates_total);
    last_update = TimeCurrent();

    int loops = g_Minimized ? 1 : SymbolCount;

    for(int s = 0; s < loops; s++) 
    {
        string sym = Symbols[s];
        string rp = PREFIX + "R" + IntegerToString(s) + "_";
        
        int bull_c = 0; int bear_c = 0; bool loading = false;

        for(int tf = 0; tf < 4; tf++) 
        {
            double b_val = GetRCMData(sym, TFs[tf], 2);
            double q_val = GetRCMData(sym, TFs[tf], 3);

            string b_txt = "----"; string q_txt = "----";
            color b_clr = Clr_Neutral; color q_clr = Clr_Neutral;

            if(b_val != EMPTY_VALUE && q_val != EMPTY_VALUE && b_val != 0) 
            {
                b_txt = (b_val > 0 ? "+" : "") + DoubleToStr(b_val, 2);
                q_txt = (q_val > 0 ? "+" : "") + DoubleToStr(q_val, 2);
                
                if(b_val > q_val) { b_clr = Clr_Bull; q_clr = Clr_Bear; bull_c++; } 
                else              { b_clr = Clr_Bear; q_clr = Clr_Bull; bear_c++; }
            } else {
                loading = true;
            }

            ObjectSetString(0, rp+"B_"+IntegerToString(tf), OBJPROP_TEXT, b_txt);
            ObjectSetInteger(0, rp+"B_"+IntegerToString(tf), OBJPROP_COLOR, b_clr);
            ObjectSetString(0, rp+"Q_"+IntegerToString(tf), OBJPROP_TEXT, q_txt);
            ObjectSetInteger(0, rp+"Q_"+IntegerToString(tf), OBJPROP_COLOR, q_clr);
        }

        string bias_txt = ""; color bias_clr = Clr_Neutral;
        if(loading) {
            bias_txt = "LOADING";
        } else if(bull_c >= 3) {
            bias_txt = "Bull("+IntegerToString(bull_c)+"/4)"; bias_clr = Clr_Bull;
        } else if(bear_c >= 3) {
            bias_txt = "Bear("+IntegerToString(bear_c)+"/4)"; bias_clr = Clr_Bear;
        } else {
            bias_txt = "Neutral"; bias_clr = Clr_Neutral;
        }

        ObjectSetString(0, rp+"BIAS", OBJPROP_TEXT, bias_txt);
        ObjectSetInteger(0, rp+"BIAS", OBJPROP_COLOR, bias_clr);
    }
    
    return(rates_total);
}
//+------------------------------------------------------------------+