function plotODG2withAnalysis(od_imgs, scan_parameter_values, g2_results, analysis_results, options, varargin)
%% plotODG2withAnalysis
% Author:       Karthik
% Date:         2025-09-30
% Version:      1.0
%
% Description:
%   For each scan parameter value, produces one or more compact figures.
%   Each row contains:
%       - Col 1: OD image
%       - Col 2: g² correlation matrix
%   Overlays from analysis (boundary, ellipse, centroid, anisotropy) can be
%   optionally toggled on/off.
%
% Inputs:
%   od_imgs              : cell array of OD images
%   scan_parameter_values: vector/array of scan parameters (one per image)
%   g2_results           : struct with fields
%                          - g2_matrices{k}
%                          - dx_phys{k}, dy_phys{k}
%   analysis_results     : struct with fields
%                          - boundary_coords{k}
%                          - ellipse_params{k}
%                          - peak_centroid{k}
%                          - anisotropy_vals(k)
%                          - roi_params{k} (optional)
%   options              : struct with imaging calibration
%                          - pixel_size (in meters)
%                          - magnification (unitless)
%   varargin             : name-value pairs
%                          - 'FontName', 'FontSize', 'SkipLivePlot', 'SkipSaveFigures',
%                            'SaveDirectory', 'ShowOverlays', 'RepsPerPage'
%
% Notes:
%   Requires conductCorrelationAnalysis and analyzeG2Structures outputs.

    % --- Parse optional name-value pairs ---
    p = inputParser;
    addParameter(p, 'FontName', 'Arial', @ischar);
    addParameter(p, 'FontSize', 12, @isnumeric);
    addParameter(p, 'SkipLivePlot', false, @islogical);
    addParameter(p, 'SkipSaveFigures', true, @islogical);
    addParameter(p, 'SaveDirectory', pwd, @ischar);
    addParameter(p, 'ShowOverlays', true, @islogical);
    addParameter(p, 'RepsPerPage', 10, @isnumeric); % pagination
    parse(p, varargin{:});
    opts = p.Results;

    % --- Setup save directory if needed ---
    if ~opts.SkipSaveFigures
        saveFolder = fullfile(opts.SaveDirectory, 'Results', 'SavedFigures', 'OD_G2_withAnalysis');
        if ~exist(saveFolder, 'dir')
            mkdir(saveFolder);
        end
    end

    % --- Group by parameter value ---
    param_vals = scan_parameter_values(:);
    unique_params = unique(param_vals, 'rows');
    N_params = size(unique_params, 1);

    for pIdx = 1:N_params
        matches = ismember(param_vals, unique_params(pIdx, :), 'rows');
        idx_list = find(matches);
        N_reps = numel(idx_list);

        % --- Pagination over repetitions ---
        numPages = ceil(N_reps / opts.RepsPerPage);
        for pageIdx = 1:numPages
            repStart  = (pageIdx-1)*opts.RepsPerPage + 1;
            repEnd    = min(pageIdx*opts.RepsPerPage, N_reps);
            repSubset = idx_list(repStart:repEnd);
            N_rows    = numel(repSubset);

            % --- Create compact figure ---
            if ~opts.SkipLivePlot
                fig = figure('Color', 'w', 'Position', [100 100 400 800]);
                figure(fig);  % ensure visible
            else
                fig = figure('Color', 'w', 'Position', [100 100 400 800], 'Visible','off'); % invisible for direct save
            end

            t = tiledlayout(fig, N_rows, 2, 'TileSpacing', 'compact', 'Padding', 'compact');
            title(t, sprintf('Scan parameter: %s | Page %d/%d', ...
                mat2str(unique_params(pIdx, :)), pageIdx, numPages), ...
                'FontSize', opts.FontSize + 2, 'FontWeight', 'bold', 'FontName', opts.FontName);

            for r = 1:N_rows
                k = repSubset(r);

                % --- OD image ---
                nexttile;
                [M, N] = size(od_imgs{k});
                x_phys = ((1:N) - ceil(N/2)) * (options.pixel_size / options.magnification * 1e6); % µm
                y_phys = ((1:M) - ceil(M/2)) * (options.pixel_size / options.magnification * 1e6); % µm
                imagesc(x_phys, y_phys, od_imgs{k});
                axis image;
                set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14);
                colormap(gca, Colormaps.inferno());
                xlabel('x (\mum)');
                ylabel('y (\mum)');
                
                % --- g² correlation matrix ---
                nexttile;
                dx = g2_results.dx_phys{k};
                dy = g2_results.dy_phys{k};
                g2_matrix = g2_results.g2_matrices{k};
                imagesc(dx, dy, g2_matrix);
                axis image; 
                set(gca, 'YDir', 'normal', 'FontName', opts.FontName, 'FontSize', 14);
                colormap(gca, Colormaps.coolwarm()); colorbar;
                xlabel('\Deltax (\mum)'); ylabel('\Deltay (\mum)');
                hold on;

                % --- Optional overlays ---
                if opts.ShowOverlays
                    boundary   = analysis_results.boundary_coords{k};
                    ellipse    = analysis_results.ellipse_params{k};
                    centroid   = analysis_results.peak_centroid{k};
                    anisotropy = analysis_results.anisotropy_vals(k);
                    theta      = analysis_results.ellipse_params{k}(5);

                    % ROI rectangle (rotated)
                    if isfield(analysis_results, 'roi_params')
                        roi = analysis_results.roi_params{k};
                        if ~isempty(roi) && all(~isnan(roi))
                            x0 = roi(1); y0 = roi(2);
                            w  = roi(3); h  = roi(4);
                            roi_theta = roi(5);
                            corners = [ -w/2, -h/2; w/2, -h/2; w/2, h/2; -w/2, h/2];
                            R = [cos(roi_theta), -sin(roi_theta); sin(roi_theta), cos(roi_theta)];
                            corners_rot = (R*corners')' + [x0, y0];
                            corners_rot = [corners_rot; corners_rot(1,:)];
                            plot(corners_rot(:,1), corners_rot(:,2), 'r--', 'LineWidth',1.5);
                        end
                    end

                    if all(~isnan(boundary(:)))
                        plot(boundary(:, 1), boundary(:, 2), 'g-', 'LineWidth', 2);
                    end
                    if all(~isnan(centroid))
                        plot(centroid(1), centroid(2), 'mo', 'MarkerSize', 8, 'LineWidth', 2);
                        x_c = centroid(1); y_c = centroid(2);
                    else
                        x_c = NaN; y_c = NaN;
                    end
                    if all(~isnan(ellipse)) && ~isnan(anisotropy) && anisotropy ~= 0
                        a   = ellipse(3); 
                        b   = ellipse(4);
                        tEllipse = linspace(0, 2*pi, 200);
                        ellipse_x = x_c + a*cos(tEllipse)*cos(theta) - b*sin(tEllipse)*sin(theta);
                        ellipse_y = y_c + a*cos(tEllipse)*sin(theta) + b*sin(tEllipse)*cos(theta);
                    
                        if anisotropy > 1.0
                            plot(ellipse_x, ellipse_y, 'y--', 'LineWidth', 2);
                        else
                            plot(ellipse_x, ellipse_y, 'y-', 'LineWidth', 2);
                        end
                    end
                end
            end

            if ~opts.SkipLivePlot
                drawnow;  % render figure
            end

            % --- Save figure ---
            if ~opts.SkipSaveFigures
                saveFileName = sprintf('OD_G2_analysis_param_%03d_page_%02d.png', pIdx, pageIdx);
                Plotter.saveFigure(fig, ...
                    'SaveFileName', saveFileName, ...
                    'SaveDirectory', saveFolder, ...
                    'SkipSaveFigures', opts.SkipSaveFigures);
            end

            % --- Close invisible figure to free memory ---
            if opts.SkipLivePlot
                close(fig);
            end
        end
    end
end
