function plotFitParameterPDF(fitResults, scanValues, paramName, varargin)
%% plotFitParameterPDF
% Author:       Karthik
% Date:         2025-10-06
% Version:      1.2
%
% Description:
%   Plots 2D PDF (heatmap) of any parameter from two-Gaussian fit results
%   for multiple scan parameters, with optional overlay of mean ± SEM.
%
% Inputs:
%   fitResults - struct array from fitTwoGaussianCurves
%   scanValues - vector of scan parameter values
%   paramName  - string specifying the parameter to plot:
%                'A1', 'mu1', 'sigma1', 'A2', 'mu2', 'sigma2'
%
% Optional name-value pairs (same as before, plus OverlayMeanSEM):
%   'OverlayMeanSEM' - logical, overlay mean ± SEM (default: true)

% --- Parse optional inputs ---
p = inputParser;
addParameter(p, 'Title', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'XLabel', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'YLabel', '', @(x) ischar(x) || isstring(x));
addParameter(p, 'FigNum', 1, @(x) isscalar(x));
addParameter(p, 'FontName', 'Arial', @ischar);
addParameter(p, 'FontSize', 14, @isnumeric);
addParameter(p, 'SkipSaveFigures', false, @islogical);
addParameter(p, 'SaveFileName', 'FitParameterPDF.fig', @ischar);
addParameter(p, 'SaveDirectory', pwd, @ischar);
addParameter(p, 'NumPoints', 200, @(x) isscalar(x));
addParameter(p, 'DataRange', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'XLim', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'YLim', [], @(x) isempty(x) || numel(x)==2);
addParameter(p, 'Colormap', @jet);
addParameter(p, 'PlotType', 'histogram', @(x) any(validatestring(x,{'kde','histogram'})));
addParameter(p, 'NumberOfBins', 50, @isscalar);
addParameter(p, 'NormalizeHistogram', true, @islogical);
addParameter(p, 'OverlayMeanSEM', true, @islogical);
parse(p, varargin{:});
opts = p.Results;

% --- Map paramName to index ---
paramMap = struct('A1',1,'mu1',2,'sigma1',3,'A2',4,'mu2',5,'sigma2',6,'residual',7);
if ~isfield(paramMap,paramName)
    error('Invalid paramName. Must be one of: A1, mu1, sigma1, A2, mu2, sigma2, residual');
end
paramIdx = paramMap.(paramName);

% --- Determine repetitions and scan parameters ---
N_params = numel(scanValues);
N_total  = numel(fitResults);
N_reps   = N_total / N_params;

% --- Extract chosen parameter values or residuals ---
paramValues = nan(N_reps, N_params);
for k = 1:N_total
    paramIdxScan = mod(k-1, N_params) + 1;
    repIdx       = floor((k-1)/N_params) + 1;

    if strcmpi(paramName, 'residual')
        % Use residual RMS stored in fitResults
        if isfield(fitResults(k), 'residual')
            paramValues(repIdx, paramIdxScan) = fitResults(k).residual;
        else
            warning('No residual_rms field in fitResults(%d); setting NaN.', k);
        end
    else
        paramValues(repIdx, paramIdxScan) = fitResults(k).pFit(paramIdx);
    end
end

% --- Create mask of true zeros (from fillZeroStruct) ---
trueZeroMask = false(size(paramValues));
for i = 1:N_total
    paramIdxScan = mod(i-1, N_params) + 1;
    repIdx       = floor((i-1)/N_params) + 1;
    if ~fitResults(i).isValid && all(fitResults(i).pFit == 0)
        trueZeroMask(repIdx, paramIdxScan) = true;
    end
end

% --- Prepare data per scan parameter ---
dataCell = cell(N_params,1);
for i = 1:N_params
    dataCell{i} = paramValues(:,i);
end

% --- Determine y-range ---
if isempty(opts.DataRange)
    allData = cell2mat(dataCell(:));
    y_min   = min(allData);
    y_max   = max(allData);
else
    y_min   = opts.DataRange(1);
    y_max   = opts.DataRange(2);
end

% --- Prepare PDF grid/matrix ---
if strcmpi(opts.PlotType,'kde')
    y_grid     = linspace(y_min, y_max, opts.NumPoints);
    pdf_matrix = zeros(numel(y_grid), N_params);
else
    edges      = linspace(y_min, y_max, opts.NumberOfBins+1);
    binCenters = (edges(1:end-1) + edges(2:end))/2;
    pdf_matrix = zeros(numel(binCenters), N_params);
end

% --- Compute PDFs ---
for i = 1:N_params
    data = dataCell{i};
    data = data(~isnan(data));
    if isempty(data), continue; end
    if strcmpi(opts.PlotType,'kde')
        f = ksdensity(data, y_grid);
        pdf_matrix(:,i) = f;
    else
        counts = histcounts(data, edges);
        if opts.NormalizeHistogram
            binWidth = edges(2) - edges(1);
            counts   = counts / (sum(counts) * binWidth);
        end
        pdf_matrix(:,i) = counts(:);
    end
end

% --- Mask out non-data regions (make them white) ---
dataMask = ~isnan(paramValues);          % where valid fits exist
maskPerScan = any(dataMask,1);           % scans that have any valid data
pdf_matrix(:, ~maskPerScan) = NaN;       % make empty scans white

% --- Plot heatmap ---
fig = figure(opts.FigNum); clf(fig);
set(fig, 'Color', 'w', 'Position', [100 100 950 750]);

if strcmpi(opts.PlotType,'kde')
    h = imagesc(scanValues, y_grid, pdf_matrix);
    set(h, 'AlphaData', ~isnan(pdf_matrix));  % make NaNs transparent
else
    h = imagesc(scanValues, binCenters, pdf_matrix);
    set(h, 'AlphaData', ~isnan(pdf_matrix));  % make NaNs transparent
end

set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', opts.FontSize);
xlabel(opts.XLabel, 'FontSize', opts.FontSize, 'FontName', opts.FontName);
ylabel(opts.YLabel, 'FontSize', opts.FontSize, 'FontName', opts.FontName);
title(opts.Title, 'FontSize', opts.FontSize+2, 'FontWeight', 'bold');

% --- Colormap and colorbar ---
cmap = feval(opts.Colormap);
colormap([1 1 1; cmap]);  % prepend white for NaN regions
c = colorbar;
if strcmpi(opts.PlotType,'kde') || opts.NormalizeHistogram
    ylabel(c, 'Probability Density', 'Rotation', -90, 'FontName', opts.FontName, 'FontSize', opts.FontSize);
else
    ylabel(c, 'Counts', 'Rotation', -90, 'FontName', opts.FontName, 'FontSize', opts.FontSize);
end

if ~isempty(opts.XLim)
    xlim(opts.XLim);
end

if ~isempty(opts.YLim)
    ylim(opts.YLim);
end

% --- Overlay mean ± SEM if requested ---
if opts.OverlayMeanSEM
    meanParam = nanmean(paramValues,1);
    semParam  = nanstd(paramValues,0,1) ./ sqrt(sum(~isnan(paramValues),1));
    hold on;
    xVec = reshape(scanValues, 1, []);  % ensures 1 × N_params
    fill([xVec, fliplr(xVec)], [meanParam - semParam, fliplr(meanParam + semParam)], ...
         [0.2 0.4 0.8], 'FaceAlpha',0.2, 'EdgeColor','none');
    plot(scanValues, meanParam, 'k-', 'LineWidth', 2);
end

end
