function [spectral_weight, g2, theta_vals] = conductFourierAnalysis(folder_path, run_index, N_bins, Threshold, Sigma, SuppressPlotFlag)
    
    arguments
        folder_path                (1,:) char 
        run_index                  (1,:) {mustBeNumeric,mustBeReal}
        N_bins                     (1,:) {mustBeNumeric,mustBeReal} 
        Threshold                  (1,:) {mustBeNumeric,mustBeReal}
        Sigma                      (1,:) {mustBeNumeric,mustBeReal} 
        SuppressPlotFlag           (1,:) logical = true
    end

    set(0,'defaulttextInterpreter','latex')
    set(groot, 'defaultAxesTickLabelInterpreter','latex'); set(groot, 'defaultLegendInterpreter','latex');
    
    % Load data
    Data   = load(fullfile(fullfile(folder_path, sprintf('Run_%03i', run_index)), 'psi_gs.mat'), 'psi', 'Transf', 'Observ', 'Params');

    Params = Data.Params;
    Transf = Data.Transf; 
    Observ = Data.Observ; 
    
    if isgpuarray(Data.psi)
        psi = gather(Data.psi);
    else
        psi = Data.psi;
    end
    if isgpuarray(Data.Observ.residual)
        Observ.residual = gather(Data.Observ.residual); 
    else
        Observ.residual = Data.Observ.residual; 
    end
    
    alpha = Params.theta;

    % Axes scaling and coordinates in micrometers
    x = Transf.x * Params.l0 * 1e6; 
    y = Transf.y * Params.l0 * 1e6;
    z = Transf.z * Params.l0 * 1e6;
    
    dz = z(2)-z(1);
    
    % Calculate frequency increment (frequency axes)
    Nx           = length(x);       % grid size along X 
    Ny           = length(y);       % grid size along Y 
    dx           = mean(diff(x));   % real space increment in the X direction (in micrometers)
    dy           = mean(diff(y));   % real space increment in the Y direction (in micrometers)
    dvx          = 1 / (Nx * dx);  % reciprocal space increment in the X direction (in micrometers^-1)
    dvy          = 1 / (Ny * dy);  % reciprocal space increment in the Y direction (in micrometers^-1)
    
    % Create the frequency axes
    vx           = (-Nx/2:Nx/2-1) * dvx;  % Frequency axis in X (micrometers^-1)
    vy           = (-Ny/2:Ny/2-1) * dvy;  % Frequency axis in Y (micrometers^-1)
    
    % Calculate maximum frequencies
    % kx_max       = pi / dx;
    % ky_max       = pi / dy;
    
    % Generate reciprocal axes
    % kx           = linspace(-kx_max, kx_max * (Nx-2)/Nx, Nx);
    % ky           = linspace(-ky_max, ky_max * (Ny-2)/Ny, Ny);
    
    % Create the Wavenumber axes
    kx           = 2*pi*vx;  % Wavenumber axis in X
    ky           = 2*pi*vy;  % Wavenumber axis in Y 
    
    % Compute probability density |psi|^2
    n                           = abs(psi).^2;
    
    nxy                         = squeeze(trapz(n*dz,3));
    
    skipPreprocessing           = true;
    skipMasking                 = true;
    skipIntensityThresholding   = true;
    skipBinarization            = true;

    %% Extract Spectral Weight and g2
    
    IMG                         = nxy;
    
    [IMGFFT, ~]                 = computeFourierTransform(IMG, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization);
    
    [theta_vals, S_theta]       = computeNormalizedAngularSpectralDistribution(IMGFFT, 10, 35, N_bins, Threshold, Sigma);
    
    spectral_weight             = trapz(theta_vals, S_theta);

    g2 = zeros(1, N_bins);  % Preallocate
    
    for dtheta = 0:N_bins-1
        profile         = S_theta;
        profile_shifted = circshift(profile, -dtheta, 2);
    
        num   = mean(profile .* profile_shifted);
        denom = mean(profile)^2;
    
        g2(dtheta+1) = num / denom - 1;
    end

    if ~SuppressPlotFlag
        figure(1);
        clf
        set(gcf,'Position',[500 100 1000 800])
        t     = tiledlayout(2, 2, 'TileSpacing', 'compact', 'Padding', 'compact'); % 1x4 grid
        font  = 'Bahnschrift';
        % Display the cropped OD image
        ax1 = nexttile;
        plotxy = pcolor(x,y,IMG');
        set(plotxy, 'EdgeColor', 'none');
        % Define normalized positions (relative to axis limits)
        x_offset = 0.025; % 5% offset from the edges
        y_offset = 0.025; % 5% offset from the edges
        % Top-right corner (normalized axis coordinates)
        hText = text(1 - x_offset, 1 - y_offset, ...
            ['\alpha = ', num2str(rad2deg(alpha), '%.1f'), '^\circ'], ...
            'Color', 'white', 'FontWeight', 'bold', ...
            'Interpreter', 'tex', 'FontSize', 20, ...
            'Units', 'normalized', ...
            'HorizontalAlignment', 'right', ...
            'VerticalAlignment', 'top');
        axis square;
        hcb = colorbar;
        hcb.Label.Interpreter = 'latex';
        colormap(gca, Helper.Colormaps.plasma())
        set(gca, 'FontSize', 14);   % For tick labels only
        hL = ylabel(hcb, 'Column Density');
        hXLabel = xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
        hYLabel = ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
        hTitle = title('$|\Psi(x,y)|^2$', 'Interpreter', 'latex', 'FontSize', 14) ;
        set(hText, 'FontName', font)
        set([hXLabel, hYLabel, hL], 'FontSize', 14)
        set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold');  % Set font and size for title
        
        
        % Plot the power spectrum
        nexttile;
        imagesc(kx, ky, log(1 + abs(IMGFFT).^2));
        axis square;
        hcb = colorbar;
        colormap(gca, Helper.Colormaps.plasma())
        set(gca, 'FontSize', 14);   % For tick labels only
        set(gca,'YDir','normal')
        hXLabel = xlabel('k_x', 'Interpreter', 'tex');
        hYLabel = ylabel('k_y', 'Interpreter', 'tex');
        hTitle  = title('Power Spectrum - S(k_x,k_y)', 'Interpreter', 'tex');
        set([hXLabel, hYLabel], 'FontName', font)
        set([hXLabel, hYLabel], 'FontSize', 14)
        set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold');  % Set font and size for title
        
        % Plot the angular distribution
        nexttile
        plot(theta_vals/pi, S_theta,'Linewidth',2);
        set(gca, 'FontSize', 14);   % For tick labels only
        hXLabel = xlabel('\theta/\pi [rad]', 'Interpreter', 'tex');
        hYLabel = ylabel('Normalized magnitude (a.u.)', 'Interpreter', 'tex');
        hTitle = title('Angular Spectral Distribution - S(\theta)', 'Interpreter', 'tex');
        set([hXLabel, hYLabel], 'FontName', font)
        set([hXLabel, hYLabel], 'FontSize', 14)
        set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold');  % Set font and size for title
        grid on
        
        nexttile
        plot(theta_vals/pi, g2, 'o-', 'LineWidth', 1.2, 'MarkerSize', 5);
        set(gca, 'FontSize', 14);
        ylim([-1.5 3.0]);  % Set y-axis limits here
        hXLabel = xlabel('$\delta\theta / \pi$', 'Interpreter', 'latex');
        hYLabel = ylabel('$g^{(2)}(\delta\theta)$', 'Interpreter', 'latex');
        hTitle  = title('Autocorrelation', 'Interpreter', 'tex');
        set([hXLabel, hYLabel], 'FontName', font)
        set([hXLabel, hYLabel], 'FontSize', 14)
        set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold');  % Set font and size for title
        grid on;
    end
end

%% Helper Functions
function [IMGFFT, IMGPR] = computeFourierTransform(I, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization)
    % computeFourierSpectrum - Computes the 2D Fourier power spectrum 
    % of binarized and enhanced lattice image features, with optional central mask.
    %
    % Inputs:
    %   I           - Grayscale or RGB image matrix
    %
    % Output:
    %   F_mag       - 2D Fourier power spectrum (shifted)
    
    if ~skipPreprocessing
        % Preprocessing: Denoise
        filtered             = imgaussfilt(I, 10);
        IMGPR                = I - filtered; % adjust sigma as needed
    else
        IMGPR                = I;
    end
    
    if ~skipMasking
        [rows, cols]     = size(IMGPR);
        [X, Y]           = meshgrid(1:cols, 1:rows);
        % Elliptical mask parameters
        cx               = cols / 2;
        cy               = rows / 2;
        
        % Shifted coordinates
        x                = X - cx;
        y                = Y - cy;
        
        % Ellipse semi-axes
        rx               = 0.4 * cols;
        ry               = 0.2 * rows;
        
        % Rotation angle in degrees -> radians
        theta_deg        = 30;        % Adjust as needed
        theta            = deg2rad(theta_deg);
        
        % Rotated ellipse equation
        cos_t            = cos(theta);
        sin_t            = sin(theta);
        
        x_rot            = (x * cos_t + y * sin_t);
        y_rot            = (-x * sin_t + y * cos_t);
        
        ellipseMask      = (x_rot.^2) / rx^2 + (y_rot.^2) / ry^2 <= 1;
        
        % Apply cutout mask
        IMGPR            = IMGPR .* ellipseMask;
    end

    if ~skipIntensityThresholding
        % Apply global intensity threshold mask
        intensity_thresh = 0.20; 
        intensity_mask   = IMGPR > intensity_thresh;
        IMGPR            = IMGPR .* intensity_mask;
    end

    if ~skipBinarization
        % Adaptive binarization and cleanup
        IMGPR            = imbinarize(IMGPR, 'adaptive', 'Sensitivity', 0.0);
        IMGPR            = imdilate(IMGPR, strel('disk', 2));
        IMGPR            = imerode(IMGPR, strel('disk', 1));
        IMGPR            = imfill(IMGPR, 'holes');
        F                = fft2(double(IMGPR)); % Compute 2D Fourier Transform
        IMGFFT           = abs(fftshift(F))';  % Shift zero frequency to center
    else
        F                = fft2(double(IMGPR)); % Compute 2D Fourier Transform
        IMGFFT           = abs(fftshift(F))';  % Shift zero frequency to center
    end
end

function [theta_vals, S_theta] = computeNormalizedAngularSpectralDistribution(IMGFFT, r_min, r_max, num_bins, threshold, sigma)
    % Apply threshold to isolate strong peaks
    IMGFFT(IMGFFT < threshold) = 0;

    % Prepare polar coordinates
    [ny, nx]  = size(IMGFFT);
    [X, Y]    = meshgrid(1:nx, 1:ny);
    cx        = ceil(nx/2); 
    cy        = ceil(ny/2);
    R         = sqrt((X - cx).^2 + (Y - cy).^2);
    Theta     = atan2(Y - cy, X - cx); % range [-pi, pi]

    % Choose radial band
    radial_mask      = (R >= r_min) & (R <= r_max);

    % Initialize the angular structure factor array
    S_theta = zeros(1, num_bins); % Pre-allocate for 180 angle bins
    % Define the angle values for the x-axis
    theta_vals = linspace(0, pi, num_bins);

    % Loop through each angle bin
    for i = 1:num_bins
        angle_start = (i-1) * pi / num_bins;
        angle_end   = i * pi / num_bins;
        
        % Define a mask for the given angle range
        angle_mask  = (Theta >= angle_start & Theta < angle_end);
        
        bin_mask    = radial_mask & angle_mask;

        % Extract the Fourier components for the given angle
        fft_angle   = IMGFFT .* bin_mask;
        
        % Integrate the Fourier components over the radius at the angle
        S_theta(i)  = sum(sum(abs(fft_angle).^2)); % sum of squared magnitudes
    end
    
    % Create a 1D Gaussian kernel
    half_width   = ceil(3 * sigma);
    x            = -half_width:half_width;
    gauss_kernel = exp(-x.^2 / (2 * sigma^2));
    gauss_kernel = gauss_kernel / sum(gauss_kernel); % normalize
    
    % Apply convolution (circular padding to preserve periodicity)
    S_theta      = conv([S_theta(end-half_width+1:end), S_theta, S_theta(1:half_width)], gauss_kernel, 'same');
    S_theta      = S_theta(half_width+1:end-half_width); % crop back to original size
    
    % Normalize to 1
    S_theta = S_theta / max(S_theta);
end

