function varargout = diagnosticFeatureDesigner(varargin)
%DIAGNOSTICFEATUREDESIGNER Visualize and compute features on signals.
%
% diagnosticFeatureDesigner opens the Diagnostic Feature Designer app or
% brings the app into focus if it is already open.
%
% diagnosticFeatureDesigner(sessionFile) opens the app and loads a
% previously saved session. The sessionFile is the name of a session data file
% on the MATLAB path that was saved from older DFD sessions. The name must be
% provided as a character array.
%
% diagnosticFeatureDesigner(dataset) opens the app and brings up the New
% session dialog with the passed-in dataset already pre-selected in the
% source dropdown of the new session dialog. It also verifies the dataset's
% compatibility with the app before opening the app or the new session
% dialog (if the app is already opened). The supported formats for the
% input(dataset) are tables, timetables, cell arrays, matrices,
% fileEnsembleDatastore objects, simulationEnsembleDatastore objects, and
% certain labeledSignalSet objects that contain signals, condition
% variables, and features.
%
% % EXAMPLE:
% % Load a tutorial dataset for the DFD app by executing the following in the
% % MATLAB command window:
% >> load dfd_Tutorial.mat;
% % Import the loaded dataTable in the MATLAB workspace as follows:
% >> diagnosticFeatureDesigner(dataTable)

% Copyright 2018-2023 The MathWorks, Inc.


% Check for required toolboxes and licenses
[ready, msgID] = predmaintguis.internal.utils.checkRequiredToolboxes;
if ~ready
    error(message(msgID, mfilename));
end

% Try to get the App handle from groot
hApp = getappdata(groot, localDFDAAppDataPropertyName());

%For opening the DFD app by passing in the dataset as input argument to the
%API
if nargin==1  && ~(isstring(varargin{1}) || ischar(varargin{1}))
    allowedDatasetClasses = {'cell', 'idfrd', 'table', 'timetable', 'double', 'labeledSignalSet'};
    if ismember(class(varargin{1}), allowedDatasetClasses) || isa(varargin{1}, 'predmaint.internal.pmdata.PMDatastore')
        datasetName = string(inputname(1));
        dataset = varargin{1};
        if strlength(datasetName) == 0
            datasetName = "inputData";    %If the dataset was passed-in as expression->Name it. Fix for g3120091.
        end
        if iscell(dataset) && size(dataset,2)>1 && isvector(dataset)    %Edge case: Only possible for cell arrays  
            dataset = dataset(:);    %convert to column if the dataset is row vector. Fix for g3120089.
        end
        try
            predmaint.internal.validateDFDInput(dataset);    %verify if the input dataset is compatible with the DFD app

            if isempty(hApp) || ~isa(hApp,'handle')    %If the app is not opened already
                hApp = localLauchAPPAndSaveAppData();
            else
                bringToFront(hApp.AppContainer);    %Bring app at the front
            end

            %Pause for the AppContainer to initial and start running (To get the New Session dialog on front)
            while string(hApp.AppContainer.State) == "INITIALIZING", pause(0.1); end

            %Initialize the New Session dialog and also check if user wants to continue with overwriting previous session (if any)
            isConfirmed = hApp.HomeTab.SessionMgmtSection.newSessionWithDataset(datasetName, dataset);
            if ~isConfirmed
                return
            end

            dlg = getNewSessionDialog(hApp.HomeTab.SessionMgmtSection);
            dlg.SourcePanel.Widgets.SourceDD.Value = datasetName;    %Change the source dropdown to select input dataset
            dlg.SourcePanel.cbSourceDD(false,false);    %Call the source dropdown callback to fill everything else in the dialog appropriately
        catch ME
            error(ME.identifier, ME.message);
        end
    else
        errMsg = string(message('predmaint:general:errInvalidInput'));
        error(errMsg);
    end
elseif nargin==1 && (isstring(varargin{1}) || ischar(varargin{1}))
    % Validate session data prior to launching the app
    [valid, S] = predmaintguis.internal.utils.utValidateAppSessionData(varargin{1});
    if valid
        % If the app is already open, confirm if user wants to load the
        % session. Geck ID: g3201914
        if ~isempty(hApp)
            bringToFront(hApp.AppContainer);    %Bring app at the front
            isConfirmed = hApp.HomeTab.SessionMgmtSection.confirmImport(hApp.DatasetManager);
            if ~isConfirmed
                return    %No action
            end
            hApp.HomeTab.SessionMgmtSection.notify('AppCloseRequested');    %Close the app
            drawnow
        end
        % If the session is valid, then S will be a valid struct
        % containing the app data
        hApp = predmaintguis.internal.utils.utLaunchAppWithSessionData(varargin{1}, S);
    else
        hApp = [];
    end
elseif (isempty(hApp) || ~isa(hApp,'handle') || ~isvalid(hApp)) && (isempty(varargin) || (nargin==2 && (isstring([varargin{2}{:}]) || ischar([varargin{2}{:}]))))
    %This case is when no args are provided to the API and also for session
    %data import
    hApp = localLauchAPPAndSaveAppData(varargin{:});
elseif ~isempty(hApp) && isempty(varargin)    %Additional defensive branch to show app if it is hidden behind other apps
    hApp.show
else
    errMsg = string(message('predmaint:general:errInvalidInput'));
    error(errMsg);
end

if nargout > 0
    varargout{1} = hApp;
end
end

%------------------------------------------------------------------------------------
function name = localDFDAAppDataPropertyName()
% localDFDAAppDataPropertyName  Name of the app-data property used to store
% the HealthIndicatorApp Object.
name = 'DiagnosticFeatureDesignerAppHandle';
end

%------------------------------------------------------------------------------------
function localRemoveAppFromAppData()
% localRemoveAppFromAppData  Remove Diagnostic Feature Designer app Object
% from app-data of groot.
if isappdata(groot, localDFDAAppDataPropertyName())
    rmappdata(groot, localDFDAAppDataPropertyName);
end
end

%------------------------------------------------------------------------------------
function hApp = localLauchAPPAndSaveAppData(varargin)
hApp = predmaintguis.internal.HealthIndicatorApp(varargin{:});
% Store the handle in groot to preserve it
setappdata(groot, localDFDAAppDataPropertyName(), hApp);

% Listen to the "close" event so that we can remove the handle from the app-data.
addlistener(hApp, 'ObjectBeingDestroyed', @(~,~)localRemoveAppFromAppData);
hApp.show
end