classdef auswertungs_app < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        UIFigure                  matlab.ui.Figure
        Button                    matlab.ui.control.Button
        Image                     matlab.ui.control.Image
        QueryBERsButton           matlab.ui.control.Button
        ROPAttenListBox           matlab.ui.control.ListBox
        FiberLenListBox_2Label_2  matlab.ui.control.Label
        isMPIListBox              matlab.ui.control.ListBox
        isMPIListBoxLabel         matlab.ui.control.Label
        UITable                   matlab.ui.control.Table
        QueryRUNInfosButton       matlab.ui.control.Button
        EqualizerListBox          matlab.ui.control.ListBox
        EqualizerListBoxLabel     matlab.ui.control.Label
        BitrateListBox            matlab.ui.control.ListBox
        BitrateListBox_2Label     matlab.ui.control.Label
        FormatListBox             matlab.ui.control.ListBox
        FormatListBox_2Label      matlab.ui.control.Label
        FiberLenListBox           matlab.ui.control.ListBox
        FiberLenListBox_2Label    matlab.ui.control.Label
        WavelengthListBox         matlab.ui.control.ListBox
        WavelengthListBoxLabel    matlab.ui.control.Label
        CodingListBox             matlab.ui.control.ListBox
        CodingListBoxLabel        matlab.ui.control.Label
        DatabaseDropDown          matlab.ui.control.DropDown
        DatabaseDropDownLabel     matlab.ui.control.Label
        ConnectButton             matlab.ui.control.Button
        basePathEditField         matlab.ui.control.EditField
        Panel                     matlab.ui.container.Panel
        basePathEditFieldLabel    matlab.ui.control.Label
    end


    properties (Access = private)
        db % db class var
        % Define properties to store the colors

        rawDataTable
        avgDataTable

        lblue    % Light blue
        dblue    % Dark blue
        lgreen   % Light green
        dgreen   % Dark green
        lred     % Light red
        dred     % Dark red
        lorange  % Light yellow
        dorange  % Dark yellow
        lpurple  % Light purple
        dpurple  % Dark purple
        lyellow  % Light orange
        dbrown  % Dark orange
    end

    methods (Access = private)

        % Store 12 colors in the app with descriptive names
        function LoadColors(app)
            % Get 12 colors from cbrewer2
            cols = cbrewer2('Paired', 12);

            % Assign descriptive names to each color
            app.lblue = cols(1, :);   % Light blue
            app.dblue = cols(2, :);   % Dark blue
            app.lgreen = cols(3, :);  % Light green
            app.dgreen = cols(4, :);  % Dark green
            app.lred = cols(5, :);    % Light red
            app.dred = cols(6, :);    % Dark red
            app.lorange = cols(7, :); % Light yellow
            app.dorange = cols(8, :); % Dark yellow
            app.lpurple = cols(9, :); % Light purple
            app.dpurple = cols(10, :); % Dark purple
            app.lyellow = cols(11, :); % Light orange
            app.dbrown = cols(12, :); % Dark orange
        end

        function refreshListBoxes(app)


            % Fetch and sort unique PAM level values, then populate PAMLevelListBox
            pamLevels = sort(app.db.distinctValues.Configurations.pam_level);
            app.FormatListBox.Items = cellstr(["all";num2str(pamLevels)]);


            % dB mode values, then populate DBModeListBox
            dbModes = sort(app.db.distinctValues.Configurations.db_mode);
            app.CodingListBox.Items = cellstr(["all";string(db_mode(dbModes))]);

            % MPI mode values, then populate app.isMPIListBox
            mpiModes = sort(app.db.distinctValues.Configurations.is_mpi);
            app.isMPIListBox.Items = cellstr(["all";num2str(mpiModes)]);


            % fiber length values, then populate FiberLenListBox
            fiberLengths = sort(app.db.distinctValues.Configurations.fiber_length);
            app.FiberLenListBox.Items = cellstr(["all";num2str(fiberLengths)]);

            % ROP attenuation values, then populate FiberLenListBox
            rop_attenuation = sort(app.db.distinctValues.Configurations.rop_attenuation );
            app.ROPAttenListBox.Items = cellstr(["all";num2str(rop_attenuation)]);


            % bitrate values, then populate BitrateListBox
            bitrates = sort(app.db.distinctValues.Configurations.bitrate);
            app.BitrateListBox.Items = cellstr(["all";num2str(bitrates)]);


            % avelength values, then populate WavelengthListBox
            wavelengths = sort(app.db.distinctValues.Configurations.wavelength);
            app.WavelengthListBox.Items = cellstr(["all";num2str(wavelengths)]);

            % EQ values, then populate WavelengthListBox
            equalizer = sort(app.db.distinctValues.Equalizer.eq_type);
            app.EqualizerListBox.Items = cellstr(["all";string(equalizer_structure(equalizer))]);


        end


        function [filterParams] = getFilterParamsFromUI(app)


            % Create filterParams struct with default values
            filterParams = app.db.tables;
            filterParams.Configurations = struct( ...
                'bitrate',          [], ...
                'db_mode',          [], ...
                'fiber_length',     [], ...
                'interference_attenuation', [], ...
                'interference_path_length', [], ...
                'is_mpi',           [], ...
                'pam_level',        [], ...
                'precomp_amp',      [], ...
                'rop_attenuation',  [], ...
                'symbolrate',       [], ...
                'v_awg',            [], ...
                'v_bias',           [], ...
                'wavelength',       [] ...
                );

            % Fill in filterParams with user choices from respective listboxes
            if ~isempty(app.FormatListBox.Value) && (app.FormatListBox.Value~="all")
                filterParams.Configurations.pam_level = str2double(app.FormatListBox.Value);
            end

            if ~isempty(app.CodingListBox.Value) && (app.CodingListBox.Value~="all")
                filterParams.Configurations.db_mode = double(db_mode(app.CodingListBox.Value));
            end

            if ~isempty(app.FiberLenListBox.Value) && (app.FiberLenListBox.Value~="all")
                filterParams.Configurations.fiber_length = str2double(app.FiberLenListBox.Value);
            end

            if ~isempty(app.ROPAttenListBox.Value) && (app.ROPAttenListBox.Value~="all")
                filterParams.Configurations.rop_attenuation = str2double(app.ROPAttenListBox.Value);
            end

            if ~isempty(app.BitrateListBox.Value)&& (app.BitrateListBox.Value~="all")
                filterParams.Configurations.bitrate = str2double(app.BitrateListBox.Value);
            end

            if ~isempty(app.WavelengthListBox.Value) && (app.WavelengthListBox.Value~="all")
                filterParams.Configurations.wavelength = str2double(app.WavelengthListBox.Value);
            end

            if ~isempty(app.EqualizerListBox.Value) && (app.EqualizerListBox.Value~="all")
                filterParams.Equalizer.eq_type = equalizer_structure(app.EqualizerListBox.Value);
            end

        end

        function selectedFields = getSelectedFields(app)

            % Define the fields to be selected in the query
            selectedFields = {'Runs.run_id', 'BERs.ber_id', 'Equalizer.eq_id', 'Equalizer.eq_type', 'BERs.ber', ...
                'BERs.occurrence', 'Configurations.db_mode', 'Configurations.pam_level', ...
                'Configurations.bitrate', 'Configurations.symbolrate', 'Configurations.fiber_length', ...
                'Configurations.wavelength', 'Configurations.precomp_amp', ...
                'Measurements.power_rop', 'Measurements.power_laser', 'Measurements.power_pd_in'};

        end

        function success = queryDB(app,filterParams, selectedFields)
            try
                % Use the query method to get the data table
                [app.rawDataTable, ~] = app.db.queryDB(filterParams, selectedFields);
            catch e
                warning('Failed to get config:%s %s ',filterParams, e.message);
            end

            if isempty(app.rawDataTable)
                success = 2;
            else
                success = 1;
            end
        end

        function buildAvgTable(app)

            try

                if ~isempty(app.rawDataTable)

                    % Filter out rows where 'ber' is "NaN"
                    validRows = (~isnan(app.rawDataTable.('ber')));

                    invalidRows = sum(validRows == 0);
                    if invalidRows > 0
                        fprintf('Found %d invalid entries \n',invalidRows);
                    end

                    filteredTable = app.rawDataTable(validRows, :);

                    % Calculate the mean BER for each combination of 'run_id' and 'eq_type'
                    groupedData = groupsummary(filteredTable, {'run_id', 'eq_type'}, 'mean', {'ber', 'power_rop','power_pd_in'});

                    % Extract unique rows from dataTable for each run_id with relevant configuration details
                    uniqueConfigFields = {'run_id', 'pam_level', 'bitrate','symbolrate', 'fiber_length', 'wavelength', 'precomp_amp', 'db_mode'};
                    [~, uniqueIdx] = unique(filteredTable.run_id);  % Get unique run_id indices
                    configDetails = filteredTable(uniqueIdx, uniqueConfigFields);  % Extract unique configurations for each run_id

                    % Prepare the join key for both groupedData and configDetails
                    groupedDataTable = table(groupedData.run_id, groupedData.eq_type, groupedData.GroupCount, groupedData.mean_ber,groupedData.mean_power_rop,groupedData.mean_power_pd_in, ...
                        'VariableNames', {'run_id', 'eq_type', 'GroupCount', 'mean_ber','mean_power_rop','mean_power_pd_in'});

                    % Join groupedData with configDetails on the run_id field
                    app.avgDataTable = join(groupedDataTable, configDetails, 'Keys', 'run_id');

                else

                    error('No BER Data');

                end

            catch ME

                % Display error message in a message window (uialert)
                uialert(app.UIFigure, ME.message, 'Error', 'Icon', 'error');

            end

        end

        function fillUITable(app, dataTable)

            % Check if dataTable is empty
            if isempty(dataTable)
                warning('Data table is empty, no data to populate in UITable.');
                app.UITable.Data = {}; % Set UITable to empty
            else
                % Convert the data table to a format that can be displayed in UITable
                % UITable in App Designer can display cell arrays or tables
                app.UITable.Data = dataTable;

                % Update column names in UITable for better user understanding
                app.UITable.ColumnName = dataTable.Properties.VariableNames;
            end

        end

    end


    % Callbacks that handle component events
    methods (Access = private)

        % Code that executes after component creation
        function startupFcn(app)
            app.LoadColors();
        end

        % Drop down opening function: DatabaseDropDown
        function DatabaseDropDownOpening(app, event)
            % Get the base path from the edit field
            folderPath = char(app.basePathEditField.Value);

            % Validate that the folder exists
            if isfolder(folderPath)
                % Get all .db files in the folder
                dbFiles = dir(fullfile(folderPath, '*.db'));

                % Extract filenames from the structure array
                filenames = {dbFiles.name};

                % Update the dropdown with the filenames
                if ~isempty(filenames)
                    app.DatabaseDropDown.Items = filenames;
                else
                    % If no files are found, set a default message
                    app.DatabaseDropDown.Items = {'No .db files found'};
                end
            else
                % If folder doesn't exist, set an error message in the dropdown
                app.DatabaseDropDown.Items = {'Invalid folder path'};
            end
        end

        % Button pushed function: ConnectButton
        function ConnectButtonPushed(app, event)
            try

                % Attempt to create the database handler
                app.db = DBHandler("pathToDB", [app.basePathEditField.Value, filesep, app.DatabaseDropDown.Value]);

                % If successful, set button color to green
                app.ConnectButton.BackgroundColor = app.dgreen;  % RGB for green

                app.refreshListBoxes();

            catch ME

                % If an error occurs, set button color to red
                app.ConnectButton.BackgroundColor = app.dred;  % RGB for red

                % Display error message in a message window (uialert)
                uialert(app.UIFigure, ME.message, 'Error', 'Icon', 'error');

            end
        end

        % Button pushed function: QueryRUNInfosButton
        function QueryRUNInfosButtonPushed(app, event)

            [filterParams] = app.getFilterParamsFromUI();
            selectedFields = app.getSelectedFields();
            success = app.queryDB(filterParams, selectedFields);
            app.fillUITable(app.rawDataTable);

            if success == 0
                app.QueryRUNInfosButton.BackgroundColor = app.dred;  % RGB for red
            elseif success == 2
                app.QueryRUNInfosButton.BackgroundColor = app.lorange;  % RGB for red
            elseif success == 1
                app.QueryRUNInfosButton.BackgroundColor = app.dgreen;  % RGB for red
            end

        end

        % Button pushed function: QueryBERsButton
        function QueryBERsButtonPushed(app, event)
            [filterParams] = app.getFilterParamsFromUI();
            selectedFields = app.getSelectedFields();
            success = app.queryDB(filterParams, selectedFields);
            app.buildAvgTable();
            app.fillUITable(app.avgDataTable);

            if success == 0
                app.QueryBERsButton.BackgroundColor = app.dred;  % RGB for red
            elseif success == 2
                app.QueryBERsButton.BackgroundColor = app.lorange;  % RGB for red
            elseif success == 1
                app.QueryBERsButton.BackgroundColor = app.dgreen;  % RGB for red
            end
        end

        % Clicked callback: BitrateListBox, EqualizerListBox, 
        % ...and 1 other component
        function ListBoxClicked(app, event)
            disp('clicked')
        end

        % Value changed function: WavelengthListBox
        function ListBoxValueChanged(app, event)
            disp('changed')
        end
    end

    % Component initialization
    methods (Access = private)

        % Create UIFigure and components
        function createComponents(app)

            % Get the file path for locating images
            pathToMLAPP = fileparts(mfilename('fullpath'));

            % Create UIFigure and hide until all components are created
            app.UIFigure = uifigure('Visible', 'off');
            app.UIFigure.Position = [100 100 1492 907];
            app.UIFigure.Name = 'MATLAB App';

            % Create basePathEditFieldLabel
            app.basePathEditFieldLabel = uilabel(app.UIFigure);
            app.basePathEditFieldLabel.HorizontalAlignment = 'right';
            app.basePathEditFieldLabel.Position = [11 5 56 22];
            app.basePathEditFieldLabel.Text = 'basePath';

            % Create Panel
            app.Panel = uipanel(app.UIFigure);
            app.Panel.Position = [2 0 1493 35];

            % Create basePathEditField
            app.basePathEditField = uieditfield(app.UIFigure, 'text');
            app.basePathEditField.Position = [25 6 390 22];
            app.basePathEditField.Value = 'C:\Users\Silas\Documents\MATLAB\Datensätze\sioe_labor\';

            % Create ConnectButton
            app.ConnectButton = uibutton(app.UIFigure, 'push');
            app.ConnectButton.ButtonPushedFcn = createCallbackFcn(app, @ConnectButtonPushed, true);
            app.ConnectButton.BackgroundColor = [0.651 0.651 0.651];
            app.ConnectButton.Position = [678 6 100 21];
            app.ConnectButton.Text = 'Connect';
            app.ConnectButton.Icon = fullfile(pathToMLAPP, 'App', 'database_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg');

            % Create DatabaseDropDownLabel
            app.DatabaseDropDownLabel = uilabel(app.UIFigure);
            app.DatabaseDropDownLabel.HorizontalAlignment = 'right';
            app.DatabaseDropDownLabel.Position = [454 6 56 22];
            app.DatabaseDropDownLabel.Text = 'Database';

            % Create DatabaseDropDown
            app.DatabaseDropDown = uidropdown(app.UIFigure);
            app.DatabaseDropDown.Items = {};
            app.DatabaseDropDown.DropDownOpeningFcn = createCallbackFcn(app, @DatabaseDropDownOpening, true);
            app.DatabaseDropDown.Position = [525 6 138 22];
            app.DatabaseDropDown.Value = {};

            % Create CodingListBoxLabel
            app.CodingListBoxLabel = uilabel(app.UIFigure);
            app.CodingListBoxLabel.HorizontalAlignment = 'right';
            app.CodingListBoxLabel.Position = [38 523 43 22];
            app.CodingListBoxLabel.Text = 'Coding';

            % Create CodingListBox
            app.CodingListBox = uilistbox(app.UIFigure);
            app.CodingListBox.Items = {};
            app.CodingListBox.Position = [25 443 99 71];
            app.CodingListBox.Value = {};

            % Create WavelengthListBoxLabel
            app.WavelengthListBoxLabel = uilabel(app.UIFigure);
            app.WavelengthListBoxLabel.HorizontalAlignment = 'right';
            app.WavelengthListBoxLabel.Position = [295 641 68 22];
            app.WavelengthListBoxLabel.Text = 'Wavelength';

            % Create WavelengthListBox
            app.WavelengthListBox = uilistbox(app.UIFigure);
            app.WavelengthListBox.Items = {''};
            app.WavelengthListBox.ValueChangedFcn = createCallbackFcn(app, @ListBoxValueChanged, true);
            app.WavelengthListBox.ClickedFcn = createCallbackFcn(app, @ListBoxClicked, true);
            app.WavelengthListBox.Position = [299 346 59 293];
            app.WavelengthListBox.Value = {};

            % Create FiberLenListBox_2Label
            app.FiberLenListBox_2Label = uilabel(app.UIFigure);
            app.FiberLenListBox_2Label.HorizontalAlignment = 'right';
            app.FiberLenListBox_2Label.Position = [140 640 56 22];
            app.FiberLenListBox_2Label.Text = 'Fiber Len';

            % Create FiberLenListBox
            app.FiberLenListBox = uilistbox(app.UIFigure);
            app.FiberLenListBox.Items = {};
            app.FiberLenListBox.Position = [145 346 45 293];
            app.FiberLenListBox.Value = {};

            % Create FormatListBox_2Label
            app.FormatListBox_2Label = uilabel(app.UIFigure);
            app.FormatListBox_2Label.HorizontalAlignment = 'right';
            app.FormatListBox_2Label.Position = [39 640 43 22];
            app.FormatListBox_2Label.Text = 'Format';

            % Create FormatListBox
            app.FormatListBox = uilistbox(app.UIFigure);
            app.FormatListBox.Items = {};
            app.FormatListBox.Position = [24 567 100 71];
            app.FormatListBox.Value = {};

            % Create BitrateListBox_2Label
            app.BitrateListBox_2Label = uilabel(app.UIFigure);
            app.BitrateListBox_2Label.HorizontalAlignment = 'right';
            app.BitrateListBox_2Label.Position = [416 641 40 22];
            app.BitrateListBox_2Label.Text = 'Bitrate';

            % Create BitrateListBox
            app.BitrateListBox = uilistbox(app.UIFigure);
            app.BitrateListBox.Items = {};
            app.BitrateListBox.ClickedFcn = createCallbackFcn(app, @ListBoxClicked, true);
            app.BitrateListBox.Position = [383 346 106 293];
            app.BitrateListBox.Value = {};

            % Create EqualizerListBoxLabel
            app.EqualizerListBoxLabel = uilabel(app.UIFigure);
            app.EqualizerListBoxLabel.HorizontalAlignment = 'right';
            app.EqualizerListBoxLabel.Position = [529 641 55 22];
            app.EqualizerListBoxLabel.Text = 'Equalizer';

            % Create EqualizerListBox
            app.EqualizerListBox = uilistbox(app.UIFigure);
            app.EqualizerListBox.Items = {''};
            app.EqualizerListBox.ClickedFcn = createCallbackFcn(app, @ListBoxClicked, true);
            app.EqualizerListBox.Position = [504 346 105 293];
            app.EqualizerListBox.Value = {};

            % Create QueryRUNInfosButton
            app.QueryRUNInfosButton = uibutton(app.UIFigure, 'push');
            app.QueryRUNInfosButton.ButtonPushedFcn = createCallbackFcn(app, @QueryRUNInfosButtonPushed, true);
            app.QueryRUNInfosButton.BackgroundColor = [0.502 0.502 0.502];
            app.QueryRUNInfosButton.Position = [25 295 266 22];
            app.QueryRUNInfosButton.Text = 'Query RUN Infos ';
            app.QueryRUNInfosButton.Icon = fullfile(pathToMLAPP, 'App', 'query_stats_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg');

            % Create UITable
            app.UITable = uitable(app.UIFigure);
            app.UITable.ColumnName = '';
            app.UITable.RowName = {};
            app.UITable.Position = [24 58 1447 213];

            % Create isMPIListBoxLabel
            app.isMPIListBoxLabel = uilabel(app.UIFigure);
            app.isMPIListBoxLabel.HorizontalAlignment = 'right';
            app.isMPIListBoxLabel.Position = [42 405 38 22];
            app.isMPIListBoxLabel.Text = 'is MPI';

            % Create isMPIListBox
            app.isMPIListBox = uilistbox(app.UIFigure);
            app.isMPIListBox.Items = {''};
            app.isMPIListBox.Position = [25 345 99 52];
            app.isMPIListBox.Value = {};

            % Create FiberLenListBox_2Label_2
            app.FiberLenListBox_2Label_2 = uilabel(app.UIFigure);
            app.FiberLenListBox_2Label_2.HorizontalAlignment = 'right';
            app.FiberLenListBox_2Label_2.Position = [214 640 65 22];
            app.FiberLenListBox_2Label_2.Text = 'ROP Atten.';

            % Create ROPAttenListBox
            app.ROPAttenListBox = uilistbox(app.UIFigure);
            app.ROPAttenListBox.Items = {};
            app.ROPAttenListBox.Position = [224 348 45 291];
            app.ROPAttenListBox.Value = {};

            % Create QueryBERsButton
            app.QueryBERsButton = uibutton(app.UIFigure, 'push');
            app.QueryBERsButton.ButtonPushedFcn = createCallbackFcn(app, @QueryBERsButtonPushed, true);
            app.QueryBERsButton.Position = [316 295 266 22];
            app.QueryBERsButton.Text = 'Query BERs ';

            % Create Image
            app.Image = uiimage(app.UIFigure);
            app.Image.Position = [11 702 598 153];
            app.Image.ImageSource = fullfile(pathToMLAPP, 'Block_Diagram.svg');

            % Create Button
            app.Button = uibutton(app.UIFigure, 'push');
            app.Button.Icon = 'C:\Users\Silas\Documents\MATLAB\imdd_simulation\projects\HighSpeedExperiment_2024\auswertung\App\query_stats_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg';
            app.Button.BackgroundColor = [0.502 0.502 0.502];
            app.Button.Position = [624 295 174 22];

            % Show the figure after all components are created
            app.UIFigure.Visible = 'on';
        end
    end

    % App creation and deletion
    methods (Access = public)

        % Construct app
        function app = auswertungs_app

            % Create UIFigure and components
            createComponents(app)

            % Register the app with App Designer
            registerApp(app, app.UIFigure)

            % Execute the startup function
            runStartupFcn(app, @startupFcn)

            if nargout == 0
                clear app
            end
        end

        % Code that executes before app deletion
        function delete(app)

            % Delete UIFigure when app is deleted
            delete(app.UIFigure)
        end
    end
end