%% ===== BEC-Droplets-Stripes Settings =====

% Specify data location to run analysis on
%%
%{
dataSources = {
    struct('sequence', 'StructuralPhaseTransition', ...
           'date', '2025/10/15', ...
           'runs', [6]) % specify run numbers as a string in "" or just as a numeric value
};
%}
%%
%{
dataSources = {
    struct('sequence', 'StructuralPhaseTransition', ...
           'date', '2025/10/16', ...
           'runs', [0]) % specify run numbers as a string in "" or just as a numeric value
};
%}

%%
%{
dataSources = {
    struct('sequence', 'StructuralPhaseTransition', ...
           'date', '2025/10/16', ...
           'runs', [1]) % specify run numbers as a string in "" or just as a numeric value
};
%}
%%
%{
dataSources = {
    struct('sequence', 'StructuralPhaseTransition', ...
           'date', '2025/10/16', ...
           'runs', [2]) % specify run numbers as a string in "" or just as a numeric value
};
%}
%%
%{
dataSources = {
    struct('sequence', 'StructuralPhaseTransition', ...
           'date', '2025/10/16', ...
           'runs', [3]) % specify run numbers as a string in "" or just as a numeric value
};
%}
%%

dataSources = {
    struct('sequence', 'TwoDGas_old', ...
           'date', '2025/11/04', ...
           'runs', [7]) % specify run numbers as a string in "" or just as a numeric value
};

options = struct();

% File paths
options.baseDataFolder     = '//DyLabNAS/Data';
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202511';
options.measurementName    = 'DropletsToStripes';
scriptFullPath             = mfilename('fullpath');
options.saveDirectory      = fileparts(scriptFullPath);

% Camera / imaging settings
options.cam                = 4;             % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
options.angle              = 0;             % angle by which image will be rotated
options.center             = [1420, 2030];
options.span               = [200, 200];
options.fraction           = [0.1, 0.1];
options.pixel_size         = 5.86e-6;       % in meters
options.magnification      = 24;
options.ImagingMode        = 'HighIntensity';
options.PulseDuration      = 5e-6;          % in s

% Fourier analysis settings
options.theta_min          = deg2rad(0);
options.theta_max          = deg2rad(180);
options.N_radial_bins      = 500;
options.Radial_Sigma       = 2;
options.Radial_WindowSize  = 5;              % odd number

options.k_min              = 1.00;         % μm⁻¹
options.k_max              = 2.25;         % μm⁻¹
options.N_angular_bins     = 360;
options.Angular_Threshold  = 75;
options.Angular_Sigma      = 5;
options.Angular_WindowSize = 5;
options.zoom_size          = 50;

% Flags
options.skipUnshuffling           = false;
options.skipNormalization         = false;

options.skipFringeRemoval         = true;
options.skipPreprocessing         = true;
options.skipMasking               = true;
options.skipIntensityThresholding = true;
options.skipBinarization          = true;

options.skipFullODImagesFolderUse = true;
options.skipSaveData              = false;
options.skipSaveFigures           = true;
options.skipSaveProcessedOD       = true;
options.skipLivePlot              = true;
options.showProgressBar           = true;

% Extras
options.font = 'Bahnschrift';
switch options.measurementName
    case 'BECToDroplets'
        options.scan_parameter        = 'rot_mag_field';
        options.flipSortOrder         = true;
        options.scanParameterUnits    = 'gauss';
        options.titleString           = 'BEC to Droplets';
    case 'BECToStripes'
        options.scan_parameter        = 'rot_mag_field';
        options.flipSortOrder         = true;
        options.scanParameterUnits    = 'gauss';
        options.titleString           = 'BEC to Stripes';
    case 'DropletsToStripes'
        options.scan_parameter        = 'ps_rot_mag_fin_pol_angle';
        options.flipSortOrder         = true;
        options.scanParameterUnits    = 'degrees';
        options.titleString           = 'Droplets to Stripes';
    case 'StripesToDroplets'
        options.scan_parameter        = 'ps_rot_mag_fin_pol_angle';
        options.flipSortOrder         = false;
        options.scanParameterUnits    = 'degrees';
        options.titleString           = 'Stripes to Droplets';
end

%% ===== Collect Images and Launch Viewer =====

[options.selectedPath, options.folderPath]                         = Helper.selectDataSourcePath(dataSources, options);

[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);

%% Conduct spectral analysis
spectral_analysis_results                                          = Analyzer.extractFullAngularSpectralDistribution(od_imgs, options);
S_theta_curves                                                     = spectral_analysis_results.S_theta_norm_all;

%% ------------------ 1. Plot of all Angular Spectral Distribution Curves ------------------
Plotter.plotSpectralCurves( ...
    S_theta_curves, ...
    spectral_analysis_results.theta_vals/pi, ...   % correct θ values
    scan_reference_values, ...                          % correct scan params
    'Title', options.titleString, ...
    'XLabel', '\theta / \pi', ...
    'YLabel', 'S(\theta)', ...
    'HighlightEvery', 10, ...              % highlight every 10th repetition
    'FontName', options.font, ...
    'FigNum', 1, ...
    'TileTitlePrefix', '\alpha', ...       % user-defined tile prefix
    'TileTitleSuffix', '^\circ', ...       % user-defined suffix (e.g., degrees symbol)
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SpectralCurves.fig', ...
    'SaveDirectory', options.saveDirectory);

%% ------------------ 2. Plot of all Angular Spectral Distribution Curves shifted ------------------

% --- Recenter curves first ---
results = Analyzer.recenterSpectralCurves(S_theta_curves, ...
                                          spectral_analysis_results.theta_vals/pi, ...
                                          scan_reference_values, ...
                                          'SearchRange', [0 90]);  % degrees

% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
thetaMin = 0;     % in units of pi (since you divided by pi)
thetaMax = 1;     % corresponds to pi/2

mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
results.x_values = results.x_values(mask);

% Apply the same mask to each curve set
for i = 1:numel(results.curves)
    results.curves{i}      = results.curves{i}(:, mask);
    results.curves_mean{i} = results.curves_mean{i}(mask);
    results.curves_error{i}= results.curves_error{i}(mask);
end

Plotter.plotSpectralCurvesRecentered( ...
    results, ...
    scan_reference_values, ...                          % correct scan params
    'Title', options.titleString, ...
    'XLabel', '\theta / \pi', ...
    'YLabel', 'S(\theta)', ...
    'HighlightEvery', 10, ...              % highlight every 10th repetition
    'FontName', options.font, ...
    'FigNum', 2, ...
    'TileTitlePrefix', '\alpha', ...       % user-defined tile prefix
    'TileTitleSuffix', '^\circ', ...       % user-defined suffix (e.g., degrees symbol)
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SpectralCurves.fig', ...
    'SaveDirectory', options.saveDirectory);

%% ------------------ 3. Plot cumulants from shifted Angular Spectral Distribution Curves ------------------
Plotter.plotSpectralDistributionCumulants(results, ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'FontName', options.font, ...
    'FontSize', 14, ...
    'FigNum', 3, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SpectralCumulants.fig', ...
    'SaveDirectory', options.saveDirectory);

%% ------------------ 4. Fit shifted Angular Spectral Distribution Curves ------------------
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
    S_theta_curves, ...
    spectral_analysis_results.theta_vals, ...
    'MaxTheta', pi/2, ...         
    'ResidualThreshold', 0.20, ...
    'PositionThreshold', pi/25, ...
    'AmplitudeThreshold', 0.15);

%% Post-fit diagnostics
% --- Determine max number of parameters across all fits ---
maxParams = max(cellfun(@numel, {fitResults.pFit}));

% --- Preallocate padded array ---
Ncurves   = numel(fitResults);
pFit_all  = nan(Ncurves, maxParams);

% --- Fill padded array ---
for k = 1:Ncurves
    pk = fitResults(k).pFit;
    pFit_all(k,1:numel(pk)) = pk;
end

isValid_all = [fitResults.isValid];  % logical vector

% --- Extract parameters of the SECOND Gaussian only ---
% For both 2G and 3G, second Gaussian = indices 4:6
pSecond = pFit_all(:, 4:6);

% --- Diagnostics ---
numWithNaN  = sum(any(isnan(pSecond), 2));
numWithZero = sum(any(pSecond == 0, 2));
numValid    = sum(isValid_all);

% Display summary
fprintf('\n--- Fit diagnostics (second Gaussian only) ---\n');
fprintf('Curves with ≥1 NaN in second Gaussian params: %d\n', numWithNaN);
fprintf('Curves with ≥1 zero in second Gaussian params: %d\n', numWithZero);
fprintf('Curves marked as isValid = true: %d\n', numValid);
fprintf('----------------------------------------\n\n');

%% ------------------ Plot Fits on Raw ------------------
options.skipLivePlot    = true;
options.skipSaveFigures = true; 
Plotter.plotTwoModeGaussianFitsOnRawASD(fitResults, rawCurves, 4, 6, ...
    'SkipLivePlot', options.skipLivePlot, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveDirectory', options.saveDirectory);

%% ------------------ Interactive Fit Viewer ------------------
Analyzer.runInteractiveASDTwoGaussianFitGUI(spectral_analysis_results);

%% ------------------ 5. Plot fit parameters - amplitude ------------------
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'A2', ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak amplitude', ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'FigNum', 4, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakAmplitudePDF.fig', ...
    'SaveDirectory', options.saveDirectory, ...
    'PlotType', 'histogram', ...
    'NumberOfBins', 20, ...
    'NormalizeHistogram', true, ...
    'Colormap', @Colormaps.coolwarm, ...
    'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
    'YLim', [0, 1.2]);

%% ------------------ 6. Plot fit parameters - position ------------------
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'mu2', ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak position (\theta, rad)', ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'FigNum', 5, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakPositionPDF.fig', ...
    'SaveDirectory', options.saveDirectory, ...
    'PlotType', 'histogram', ...
    'NumberOfBins', 20, ...
    'NormalizeHistogram', true, ...
    'Colormap', @Colormaps.coolwarm, ...
    'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
    'YLim', [0, 1.6]);

%% ------------------ 7. Plot fit parameters - width ------------------
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'sigma2', ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak width (\sigma, rad)', ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'FigNum', 6, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakWidthPDF.fig', ...
    'SaveDirectory', options.saveDirectory, ...
    'PlotType', 'histogram', ...
    'NumberOfBins', 20, ...
    'NormalizeHistogram', true, ...
    'Colormap', @Colormaps.coolwarm, ...
    'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
    'YLim', [0, 1.6]);

%% ------------------ 7. Plot fit residuals ------------------
Plotter.plotFitParameterPDF(fitResults, scan_reference_values, 'residual', ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Fit Residuals', ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'FigNum', 7, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'FitResiduals.fig', ...
    'SaveDirectory', options.saveDirectory, ...
    'PlotType', 'histogram', ...
    'NumberOfBins', 20, ...
    'NormalizeHistogram', true, ...
    'Colormap', @Colormaps.coolwarm, ...
    'XLim', [min(scan_reference_values), max(scan_reference_values)], ...
    'YLim', [0, 0.3]);

%% ------------------ 9. Plot fit parameters of mean shifted Angular Spectral Distribution Curves ------------------

% --- Recenter curves first ---
results = Analyzer.recenterSpectralCurves(S_theta_curves, ...
                                          spectral_analysis_results.theta_vals/pi, ...
                                          scan_reference_values, ...
                                          'SearchRange', [0 90]);  % degrees

% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
thetaMin = 0;     % in units of pi (since you divided by pi)
thetaMax = 1;     % corresponds to pi/2

mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
results.x_values = results.x_values(mask);

% Apply the same mask to each curve set
for i = 1:numel(results.curves)
    results.curves_mean{i} = results.curves_mean{i}(mask);
end

% --- Fit two-Gaussian model to mean curves ---
[fitResultsMean, ~] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
    results.curves_mean, ...
    results.x_values*pi, ...
    'MaxTheta', pi/2, ...         
    'ResidualThreshold', 0.27, ...
    'PositionThreshold', pi/25, ...
    'AmplitudeThreshold', 0.15, ...
    'RecenterCurves', false);

% --- Prepare parameter values ---
N_params    = numel(fitResultsMean);
amp2_vals   = nan(1, N_params);
mu2_vals    = nan(1, N_params);
sigma2_vals = nan(1, N_params);

for i = 1:N_params
    pFit = fitResultsMean(i).pFit;
    if all(~isnan(pFit))
        % Successful fit → use fitted values
        amp2_vals(i)   = pFit(4);
        mu2_vals(i)    = pFit(5);
        sigma2_vals(i) = pFit(6);

    else
        % Fit failed → leave as NaN (skipped automatically)
        continue;
    end
end

% --- Plot peak amplitude mean with SEM ---
Plotter.plotMeanWithSE(scan_reference_values, amp2_vals, ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak amplitude', ...
    'FigNum', 8, ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakAmplitudeMeanWithSEM.fig', ...
    'SaveDirectory', options.saveDirectory);

% --- Plot peak position mean with SEM ---
Plotter.plotMeanWithSE(scan_reference_values, mu2_vals, ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak position (\theta, rad)', ...
    'FigNum', 9, ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakPositionMeanWithSEM.fig', ...
    'SaveDirectory', options.saveDirectory);

% --- Plot peak width mean with SEM ---
Plotter.plotMeanWithSE(scan_reference_values, sigma2_vals, ...
    'Title', options.titleString, ...
    'XLabel', '\alpha (degrees)', ...
    'YLabel', 'Secondary peak width (\sigma, rad)', ...
    'FigNum', 10, ...
    'FontName', options.font, ...
    'FontSize', 16, ...
    'SkipSaveFigures', options.skipSaveFigures, ...
    'SaveFileName', 'SecondaryPeakWidthMeanWithSEM.fig', ...
    'SaveDirectory', options.saveDirectory);

%% ------------------ 10. Compare mean of individual fits vs fit of mean ------------------
paramMap    = struct('A1',1,'mu1',2,'sigma1',3,'A2',4,'mu2',5,'sigma2',6);
paramNames  = {'A2', 'mu2', 'sigma2'};
paramLabels = {'Secondary peak amplitude', 'Secondary peak position (\theta, rad)', 'Secondary peak width (\sigma, rad)'};

meanOfIndividualFits = struct();
fitOfMeanCurve       = struct();

for p = 1:numel(paramNames)
    paramName  = paramNames{p};
    paramLabel = paramLabels{p};
    paramIdx   = paramMap.(paramName);
    
    % --- Mean of individual fits per scan_reference_values ---
    meanVals = nan(size(scan_reference_values));  % already unique
    for k = 1:numel(scan_reference_values)
        thisAlpha = scan_reference_values(k);
        mask = scan_parameter_values == thisAlpha;   % pick all repetitions
        
        pvals = arrayfun(@(f) f.pFit(paramIdx), fitResults(mask), 'UniformOutput', true);
        meanVals(k) = mean(pvals(~isnan(pvals) & isfinite(pvals)), 'omitnan');
    end
    meanOfIndividualFits.(paramName) = meanVals;
    
    % --- Fit of mean curve ---
    switch paramName
        case 'A2'
            fitOfMeanCurve.(paramName) = amp2_vals;
        case 'mu2'
            fitOfMeanCurve.(paramName) = mu2_vals;
        case 'sigma2'
            fitOfMeanCurve.(paramName) = sigma2_vals;
    end

    % --- Plot comparison ---
    fig = figure('Name', 'Comparison', 'NumberTitle', 'off');
    set(fig, 'Color', 'w', 'Position', [100 100 950 750]);
    
    hold on;
    plot(scan_reference_values, meanOfIndividualFits.(paramName), 'o-', 'LineWidth', 1.8, ...
        'DisplayName', 'Mean of Individual Fits');
    plot(scan_reference_values, fitOfMeanCurve.(paramName), 's--', 'LineWidth', 1.8, ...
        'DisplayName', 'Fit of Mean Curve');
    hold off;
    ylim([0, 1.1])
    xlabel('\alpha (degrees)', 'FontName', options.font, 'FontSize', 16);
    ylabel(paramLabel, 'FontName', options.font, 'FontSize', 16);
    title(options.titleString, 'FontName', options.font, 'FontSize', 16);
    legend('Location', 'northeast');
    grid on;
    set(gca, 'FontSize', 14);
    
    if ~options.skipSaveFigures
        saveas(fig, fullfile(options.saveDirectory, ['Compare_' paramName '.fig']));
        exportgraphics(fig, fullfile(options.saveDirectory, ['Compare_' paramName '.png']), 'Resolution', 300);
    end
end

%% ------------------ 11. Save Fit Results ------------------
outputDir = fullfile(options.saveDirectory, 'DToS_ASDFitData');
if ~exist(outputDir, 'dir')
    mkdir(outputDir);
end

% --- Define filenames for each variable ---
file_scanParamVals   = fullfile(outputDir, 'scan_parameter_values');
file_scanRefVals     = fullfile(outputDir, 'scan_reference_values');
file_fitResults      = fullfile(outputDir, 'fitResults.mat');
file_rawCurves       = fullfile(outputDir, 'rawCurves.mat');
file_spectralResults = fullfile(outputDir, 'spectral_analysis_results.mat');
file_meanFits        = fullfile(outputDir, 'fitOfMeanCurve.mat');
file_indivFits       = fullfile(outputDir, 'meanOfIndividualFits.mat');

% --- Save each variable separately ---
save(file_scanParamVals, 'scan_parameter_values', '-v7.3');
save(file_scanRefVals, 'scan_reference_values', '-v7.3');
save(file_fitResults, 'fitResults', '-v7.3');
save(file_rawCurves, 'rawCurves', '-v7.3');
save(file_spectralResults, 'spectral_analysis_results', '-v7.3');
save(file_meanFits, 'fitOfMeanCurve', '-v7.3');
save(file_indivFits, 'meanOfIndividualFits', '-v7.3');

fprintf('\n--- Fit data saved successfully ---\n');
fprintf('scanParamVals:           %s\n', file_scanParamVals);
fprintf('scanRefVals:             %s\n', file_scanRefVals);
fprintf('fitResults:              %s\n', file_fitResults);
fprintf('rawCurves:               %s\n', file_rawCurves);
fprintf('spectral_analysis_results:%s\n', file_spectralResults);
fprintf('fitOfMeanCurve:          %s\n', file_meanFits);
fprintf('meanOfIndividualFits:    %s\n', file_indivFits);
fprintf('----------------------------------\n\n');


%% Inspect individual realizations of a single parameter

% --- Recenter curves first ---
results = Analyzer.recenterSpectralCurves( ...
    S_theta_curves, ...
    spectral_analysis_results.theta_vals/pi, ...
    scan_reference_values, ...
    'SearchRange', [0 90]);  % degrees

% --- Restrict to desired theta range (e.g., 0 to 0.5*pi) ---
thetaMin = 0;     % in units of pi (since you divided by pi)
thetaMax = 1;     % corresponds to pi/2

mask = results.x_values >= thetaMin & results.x_values <= thetaMax;
results.x_values = results.x_values(mask);

% --- Apply the same mask to each curve set (1x10 cell, each 60x180) ---
for i = 1:numel(results.curves)
    results.curves{i} = results.curves{i}(:, mask);
end

% --- Convert selected curve set (e.g., 5th) into 1x60 cell array of 1xN row vectors ---
paramIdx      = 1;  % <-- choose which scan point or curve set to analyze
curves_matrix = results.curves{paramIdx};          % 60xN numeric
curves_cell   = num2cell(curves_matrix, 2);       % 1x60 cell array
curves_cell   = cellfun(@(x) x(:).', curves_cell, 'UniformOutput', false);  % ensure 1xN row vectors

% --- Fit two-Gaussian model to these curves ---
[fitResults, rawCurves] = Analyzer.fitTwoGaussianCurvesToAngularSpectralDistribution(...
    curves_cell, ...
    results.x_values*pi, ...
    'MaxTheta', pi/2, ...         
    'ResidualThreshold', 0.27, ...
    'PositionThreshold', pi/25, ...
    'AmplitudeThreshold', 0.15, ...
    'RecenterCurves', false);
