function contourSameOthers(T, xVar, yVar, zVar, fixed, varargin)
% contourSameOthers
% Contour plot di zVar in funzione di xVar e yVar usando SOLO righe dove
% tutti gli altri parametri (fixed) sono uguali.
%
% fixed: containers.Map (chiavi = nomi colonne della table)
%
% Opzioni (Name-Value):
%   'Tol'        containers.Map() tolleranze per match (default vuota)
%   'Filled'     true|false (default true)    % contourf vs contour
%   'ShowPoints' true|false (default true)    % mostra i punti reali sopra
%   'Method'     'linear'|'natural'|'nearest' (default 'linear') % se serve interpolare
%   'GridN'      int (default 80)             % griglia per interpolazione
%   'Levels'     int (default 20)

p = inputParser;
p.addParameter('Tol', containers.Map(), @(x) isa(x,'containers.Map'));
p.addParameter('Filled', true, @(x) islogical(x) && isscalar(x));
p.addParameter('ShowPoints', true, @(x) islogical(x) && isscalar(x));
p.addParameter('Method', "linear", @(s) any(strcmpi(string(s),["linear","natural","nearest"])));
p.addParameter('GridN', 80, @(n) isnumeric(n) && isscalar(n) && n>=20);
p.addParameter('Levels', 20, @(n) isnumeric(n) && isscalar(n) && n>=5);
p.parse(varargin{:});
opt = p.Results;

xVar = string(xVar);
yVar = string(yVar);
zVar = string(zVar);

vars = string(T.Properties.VariableNames);
fixedKeys = string(keys(fixed));

needed = [xVar, yVar, zVar, fixedKeys];
missing = needed(~ismember(needed, vars));
if ~isempty(missing)
    error("Colonne mancanti in TAB: %s", strjoin(missing, ", "));
end

% Non fissare per errore le variabili che si vogliono variare
if mapHasKey(fixed, xVar), remove(fixed, char(xVar)); end
if mapHasKey(fixed, yVar), remove(fixed, char(yVar)); end

% aggiorna le chiavi dopo eventuale remove
fixedKeys = string(keys(fixed));

% ---- filtro righe con parametri fissi ----
mask = true(height(T),1);

for i = 1:numel(fixedKeys)
    v = fixedKeys(i);
    col = T.(char(v));
    val = mapGet(fixed, v);

    tol = 0;
    if mapHasKey(opt.Tol, v)
        tol = mapGet(opt.Tol, v);
    end

    if isnumeric(col)
        if tol == 0
            mask = mask & (col == val);
        else
            mask = mask & (abs(col - val) <= tol);
        end
    else
        mask = mask & (string(col) == string(val));
    end
end

Ts = T(mask,:);
if height(Ts) == 0
    warning("Nessuna riga trovata con i fixed dati. Prova a rilassare fixed o usare Tol.");
    return;
end

x = Ts.(char(xVar));
y = Ts.(char(yVar));
z = Ts.(char(zVar));

m = isfinite(x) & isfinite(y) & isfinite(z);
x = x(m);
y = y(m);
z = z(m);

if numel(x) < 4
    warning("Troppi pochi punti (%d) per un contour affidabile.", numel(x));
    return;
end

% ---- prova a vedere se hai una griglia completa (dati su livelli discreti) ----
ux = unique(x);
uy = unique(y);
nx = numel(ux);
ny = numel(uy);

% mappa (x,y) -> valore medio di z (se duplicati)
G = groupsummary(table(x,y,z), ["x","y"], "mean", "z");
% G ha colonne: x, y, mean_z
haveGrid = (height(G) == nx*ny);

figure('Name', sprintf('%s contour (fixed others)', char(zVar)));
hold on;
grid on;
box on;

if haveGrid
    % ricostruisci matrice Z su griglia
    [Xg, Yg] = meshgrid(ux, uy);
    Zg = nan(ny, nx);

    for ii = 1:height(G)
        ix = find(ux == G.x(ii), 1);
        iy = find(uy == G.y(ii), 1);
        Zg(iy, ix) = G.mean_z(ii);
    end

    if opt.Filled
        contourf(Xg, Yg, Zg, opt.Levels);
    else
        contour(Xg, Yg, Zg, opt.Levels, 'LineWidth', 2);
    end
else
    % griglia incompleta -> interpolazione (più robusta)
    xn = linspace(min(x), max(x), opt.GridN);
    yn = linspace(min(y), max(y), opt.GridN);
    [Xg, Yg] = meshgrid(xn, yn);

    F = scatteredInterpolant(x, y, z, char(opt.Method), 'none');
    Zg = F(Xg, Yg);

    if opt.Filled
        contourf(Xg, Yg, Zg, opt.Levels);
    else
        contour(Xg, Yg, Zg, opt.Levels, 'LineWidth', 2);
    end
end

cb = colorbar;
ylabel(cb, char(zVar), 'Interpreter','none',FontSize = 30);

if opt.ShowPoints
    scatter(x, y, 20, 'k', 'filled', ...
        'MarkerFaceAlpha', 0.35, 'MarkerEdgeAlpha', 0.35);
end

xlabel(char(xVar), 'Interpreter','none','FontSize',30);
ylabel(char(yVar), 'Interpreter','none','FontSize',30);

ax = gca;
ax.FontSize = 25;      % numeri sugli assi più grandi
ax.LineWidth = 2;    % assi un po' più marcati

% titolo con condizione fissa
parts = strings(1, numel(fixedKeys));
for i = 1:numel(fixedKeys)
    parts(i) = fixedKeys(i) + "=" + string(mapGet(fixed, fixedKeys(i)));
end

%title(strjoin([zVar + " | ", parts], "  "), 'Interpreter','none');

hold off;
end

function tf = mapHasKey(M, key)
    tf = isKey(M, char(string(key)));
end

function val = mapGet(M, key)
    val = M(char(string(key)));
end