function results = conductCorrelationAnalysis(od_imgs, scan_parameter_values, options)
%% conductCorrelationAnalysis
% Author:       Karthik
% Date:         2025-09-14
% Version:      1.0
%
% Description:
%   Computes 2D autocorrelation g²(Δx,Δy) on OD images.
%   Extracts radial and angular distributions using Calculator functions.
%   Optionally plots results and saves figures.
%
% Inputs:
%   od_imgs                  - cell array of OD images
%   scan_parameter_values    - array of scan parameter values
%   options - struct with fields:
%       saveDirectory        - base directory to save results
%       skipSaveFigures      - skip saving plots
%       skipLivePlot         - skip live plotting
%       pixel_size           - physical pixel size of camera sensor (m)
%       magnification        - imaging magnification
%       maximumShift         - maximum pixel shift for g²
%       font                 - font name for plots
%
% Outputs:
%   results - struct containing g² maps, radial and angular distributions
%
% Notes:
%   Optional notes, references.

    %% ===== Unpack struct arguments =====
    pixel_size       = options.pixel_size;
    magnification    = options.magnification;
    skipLivePlot     = options.skipLivePlot;
    skipSaveFigures  = options.skipSaveFigures;
    saveDirectory    = options.saveDirectory;
    font             = options.font;

    radial_theta       = options.Radial_Theta;
    radial_window_size = options.Radial_WindowSize;
    N_angular_bins     = options.N_angular_bins;
    r_min              = options.Radial_Minimum;
    r_max              = options.Radial_Maximum;

    if isfield(options, 'maximumShift') && ~isempty(options.maximumShift)
        maximumShift = options.maximumShift;
    else
        maximumShift = 5; % [µm]
    end

    % --- Handle units ---
    if ischar(options.scanParameterUnits) || isstring(options.scanParameterUnits)
        unitList = {char(options.scanParameterUnits)};
    else
        unitList = options.scanParameterUnits;
    end

    %% ===== Initialization =====
    N_shots     = numel(od_imgs);
    g2_matrices = cell(1, N_shots);
    g2_radial   = cell(1, N_shots);
    g2_angular  = cell(1, N_shots);
    r_vals      = cell(1, N_shots);
    theta_vals  = cell(1, N_shots);
    dx_phys_ind = cell(1, N_shots);
    dy_phys_ind = cell(1, N_shots);


    dx          = pixel_size / magnification;
    shifts      = -maximumShift:maximumShift;

    if ~skipSaveFigures
        saveFolder = fullfile(saveDirectory, 'Results', 'SavedFigures', 'AutocorrAnalysis');
        if ~exist(saveFolder, 'dir')
            mkdir(saveFolder);
        end
    end

    %% ===== Loop over images =====
    for k = 1:N_shots
        IMG = od_imgs{k};

        % Compute g² in physical units
        [g2_matrix, dx_phys, dy_phys] = Calculator.compute2DAutocorrelation( ...
            IMG, maximumShift, pixel_size, magnification);

        g2_matrices{k} = g2_matrix;
        dx_phys_ind{k} = dx_phys;
        dy_phys_ind{k} = dy_phys;

        % Extract radial profile (within angular window)
        [r_vals{k}, g2_radial{k}] = Calculator.computeRadialCorrelation( ...
            g2_matrix, dx_phys, dy_phys, radial_theta);

        % Extract angular profile (within radial band)
        [theta_vals{k}, g2_angular{k}] = Calculator.computeAngularCorrelation( ...
            g2_matrix, dx_phys, dy_phys, r_min, r_max, N_angular_bins);
        
        % Smooth radial profile
        g2_radial_smoothed = movmean(g2_radial{k}, radial_window_size);

        
        %% ===== Plotting =====
        if ~skipLivePlot
            figure(1); clf
            set(gcf,'Position',[500 100 1000 800])
            tiledlayout(2,2,'TileSpacing','compact','Padding','compact');

            % OD image
            ax1 = nexttile;
            imagesc(IMG);
            axis equal tight;
            set(gca,'FontSize',14,'YDir','normal');
            colormap(ax1, Colormaps.inferno());
            hcb = colorbar;
            ylabel(hcb,'Optical Density','Rotation',-90,'FontSize',14,'FontName',font);
            xlabel('x [px]','FontSize',14,'FontName',font);
            ylabel('y [px]','FontSize',14,'FontName',font);
            title('OD Image','FontSize',16,'FontWeight','bold','FontName',font);

            % Annotate scan parameter
            if iscell(scan_parameter_values)
                param_row = scan_parameter_values{k};
            else
                param_row = scan_parameter_values(k,:);
            end
            if numel(unitList) < numel(param_row)
                unitList(end+1:numel(param_row)) = {''};
            end
            xPos = 0.975; yPos = 0.975; yStep = 0.075;
            for j = 1:numel(param_row)
                [unitSuffix, txtInterpreter] = getUnitInfo(unitList{j});
                text(xPos, yPos-(j-1)*yStep, sprintf('%.2f%s',param_row(j),unitSuffix), ...
                    'Color','white','FontWeight','bold','FontSize',14, ...
                    'Interpreter',txtInterpreter,'Units','normalized', ...
                    'HorizontalAlignment','right','VerticalAlignment','top');
            end

            % g² map
            ax2 = nexttile;
            imagesc(shifts, shifts, g2_matrix);
            axis equal tight;
            set(gca,'FontSize',14,'YDir','normal');
            colormap(ax2, Colormaps.coolwarm());
            colorbar;
            xlabel('\Deltax (\mum)','FontSize',14,'FontName',font);
            ylabel('\Deltay (\mum)','FontSize',14,'FontName',font);
            title('Autocorrelation g_2(\Deltax,\Deltay)','FontSize',16,'FontWeight','bold','FontName',font);

            % Radial distribution
            ax3 = nexttile;
            plot(r_vals{k}, g2_radial_smoothed, 'LineWidth',2);
            set(gca,'FontSize',14);
            set(gca, 'FontSize', 14, 'XLim', [min(r_vals{k}), max(r_vals{k})], 'YLim', [0, 1]);
            xlabel('r [\mum]','Interpreter','tex','FontSize',14,'FontName',font);
            ylabel('g_2(r)','FontSize',14,'FontName',font);
            title('Radial Distribution','FontSize',16,'FontWeight','bold','FontName',font);
            grid on;

            % Angular distribution
            ax4 = nexttile;
            plot(theta_vals{k}/pi, g2_angular{k}, 'LineWidth',2);
            set(gca,'FontSize', 14, 'YLim', [0, 1]);
            xlabel('\theta/\pi [rad]','Interpreter','tex','FontSize',14,'FontName',font);
            ylabel('g_2(\theta)','FontSize',14,'FontName',font);
            title('Angular Distribution','FontSize',16,'FontWeight','bold','FontName',font);
            grid on;
            ax = gca;
            ax.MinorGridLineStyle = ':';
            ax.MinorGridColor = [0.7 0.7 0.7];
            ax.MinorGridAlpha = 0.5;
            ax.XMinorGrid = 'on';
            ax.YMinorGrid = 'on';
        end

        %% ===== Save figures =====
        if ~skipSaveFigures && ~skipLivePlot
            fileNamePNG = fullfile(saveFolder, sprintf('g2_analysis_img_%03d.png', k));
            print(gcf, fileNamePNG, '-dpng','-r100');
        elseif ~skipLivePlot
            pause(0.5);
        end
    end

    %% ===== Package results =====
    results = struct();
    results.g2_matrices           = g2_matrices;
    results.g2_radial             = g2_radial;
    results.g2_angular            = g2_angular;
    results.r_vals                = r_vals;
    results.theta_vals            = theta_vals;
    results.dx_phys               = dx_phys_ind;
    results.dy_phys               = dy_phys_ind;
    results.scan_parameter_values = unique(scan_parameter_values);

end

%% === Local helper function ===
function [unitSuffix, txtInterpreter] = getUnitInfo(u)
    switch lower(u)
        case {'degrees','deg','°'}
            unitSuffix     = '^\circ';
            txtInterpreter = 'tex';
        case {'gauss','g'}
            unitSuffix     = ' G';
            txtInterpreter = 'none';
        otherwise
            unitSuffix     = '';
            txtInterpreter = 'none';
    end
end
