classdef MimGuiDataset < CoreBaseClass
    % MimGuiDataset. Handles the interaction between the GUI and the MIM interfaces
    %
    %
    %     You do not need to modify this file. To add new functionality, create
    %     new plguins in the Plugins and GuiPlugins folders.
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (SetAccess = private)
        CurrentContext
        GuiDatasetState
    end

    properties (Access = private)
        Dataset
        Gui
        ModeSwitcher
        MimMain
        Reporting
        Settings
        AppDef
        ContextDef
        ManualSegmentationsChangedListener
        MarkersChangedListener
        PreviewImageChangedListener
    end

    methods
        function obj = MimGuiDataset(app_def, gui, viewer_panel, settings, reporting)
            obj.AppDef = app_def;
            obj.ContextDef = app_def.GetContextDef;
            obj.GuiDatasetState = MimGuiDatasetState();
            obj.ModeSwitcher = MimModeSwitcher(viewer_panel, obj, app_def, settings, reporting);

            obj.Gui = gui;
            obj.Reporting = reporting;
            obj.Settings = settings;
            obj.MimMain = MimMain(app_def.GetFrameworkAppDef, reporting);
            obj.AddEventListener(obj.GetImageDatabase, 'SeriesHasBeenDeleted', @obj.SeriesHasBeenDeleted);
            obj.AddEventListener(obj.ModeSwitcher, 'ModeChangedEvent', @obj.ModeHasChanged);
        end

        function ChangeMode(obj, mode)
            obj.ModeSwitcher.SwitchMode(mode, obj.Dataset, obj.GuiDatasetState.CurrentPluginInfo, obj.GuiDatasetState.CurrentPluginName, obj.GuiDatasetState.CurrentVisiblePluginName, obj.CurrentContext, obj.GuiDatasetState.CurrentSegmentationName);
        end

        function ModeAutoSave(obj)
            obj.ModeSwitcher.ModeAutoSave();
        end

        function AutoSaveMarkers(obj)
            obj.Gui.AutoSaveMarkers();
        end

        function mode = GetMode(obj)
            mode = obj.ModeSwitcher.CurrentMode;
            if isempty(mode)
                obj.Reporting.Error('MimGuiDataset::NoMode', 'The operation is not possible in this mode');
            end
        end

        function mode = GetCurrentModeName(obj)
            mode = obj.ModeSwitcher.CurrentModeString;
        end

        function mode = GetCurrentSubModeName(obj)
            mode = obj.ModeSwitcher.GetSubModeName;
        end

        function is_dataset = DatasetIsLoaded(obj)
            is_dataset = ~isempty(obj.Dataset);
        end

        function is_dataset = IsPluginResultLoaded(obj)
            is_dataset = ~isempty(obj.Dataset);
        end

        function image_database = GetImageDatabase(obj)
            image_database = obj.MimMain.GetImageDatabase();
        end

        function linked_recorder = GetLinkedRecorder(obj)
            linked_recorder = obj.MimMain.FrameworkSingleton.GetLinkedDatasetRecorder();
        end

        function [uids, patient_ids] = ImportDataRecursive(obj, folder_path)
            [uids, patient_ids] = obj.MimMain.ImportDataRecursive(folder_path);
        end

        function [sorted_paths, sorted_uids] = GetListOfPaths(obj)
            [sorted_paths, sorted_uids] = obj.MimMain.ImageDatabase.GetListOfPaths();
        end

        function template_image = GetTemplateImage(obj)
            template_image = obj.Dataset.GetTemplateImage(PTKContext.OriginalImage);
        end

        function SaveMarkers(obj, name, markers)
            if obj.DatasetIsLoaded()
                obj.Dataset.SaveMarkerPoints(name, markers);
            end
        end

        function markers = LoadMarkers(obj, name)
            markers = obj.Dataset.LoadMarkerPoints(name);
        end

        function DeleteMarkerSet(obj, name)
            if obj.DatasetIsLoaded()
                obj.Dataset.DeleteMarkerSet(name);
            end
        end

        function SaveManualSegmentation(obj, name, segmentation)
            if obj.DatasetIsLoaded()
                obj.Dataset.SaveManualSegmentation(name, segmentation);
            end
        end

        function segmentation = LoadManualSegmentation(obj, segmentation_name)
            if obj.DatasetIsLoaded()
                segmentation = obj.Dataset.LoadManualSegmentation(segmentation_name, []);
            else
                segmentation = [];
            end
        end

        function DeleteManualSegmentation(obj, name)
            if obj.DatasetIsLoaded()
                obj.Dataset.DeleteManualSegmentation(name);
                if strcmp(name, obj.GuiDatasetState.CurrentSegmentationName)
                    obj.Gui.DeleteOverlays();
                end
            end
        end

        function dataset_cache_path = GetDatasetCachePath(obj)
            if obj.DatasetIsLoaded()
                dataset_cache_path = obj.Dataset.GetDatasetCachePath();
            else
                dataset_cache_path = obj.MimMain.GetDirectories.GetCacheDirectory();
            end
        end

        function dataset_cache_path = GetEditedResultsPath(obj)
            if obj.DatasetIsLoaded()
                dataset_cache_path = obj.Dataset.GetEditedResultsPath();
            else
                dataset_cache_path = obj.MimMain.GetDirectories.GetEditedResultsDirectoryAndCreateIfNecessary();
            end
        end

        function dataset_cache_path = GetOutputPath(obj)
            if obj.DatasetIsLoaded()
                dataset_cache_path = obj.Dataset.GetOutputPath();
            else
                dataset_cache_path = obj.MimMain.GetDirectories.GetOutputDirectoryAndCreateIfNecessary();
            end
        end

        function image_info = GetImageInfo(obj)
            if obj.DatasetIsLoaded()
                image_info = obj.Dataset.GetImageInfo();
            else
                image_info = [];
            end
        end

        function patint_name = GetPatientName(obj)
            if obj.DatasetIsLoaded()
                patint_name = obj.Dataset.GetPatientName();
            else
                patint_name = [];
            end
        end

        function ClearCacheForThisDataset(obj)
            if obj.DatasetIsLoaded()
                obj.Dataset.ClearCacheForThisDataset(false);
                obj.Gui.UpdateGuiForNewDataset([]);
            end
        end

        function RefreshPlugins(obj)
            obj.Gui.RefreshPluginsForDataset(obj.Dataset)
        end

        function currently_loaded_image_UID = GetUidOfCurrentDataset(obj)
            currently_loaded_image_UID = obj.GuiDatasetState.CurrentSeriesUid;
        end

        function ClearDataset(obj)
            try
                obj.ModeSwitcher.UpdateCurrentMode([], [], [], [], []);
                obj.Gui.ClearImages;
                delete(obj.Dataset);

                obj.Dataset = [];

                obj.Settings.SetLastImageInfo([], obj.Reporting);

                obj.SetNoDataset(false);
                obj.Gui.UpdateGuiForNewDataset([]);

            catch exc
                if MimErrors.IsErrorCancel(exc.identifier)
                    obj.Reporting.ShowMessage('MimGuiDataset:LoadingCancelled', 'User cancelled');
                else
                    obj.Reporting.ShowMessage('MimGuiDataset:ClearDatasetFailed', ['Failed to clear dataset due to error: ' exc.message]);
                end
            end
        end

        function ClearDatasetKeepPatient(obj)
            try
                obj.ModeSwitcher.UpdateCurrentMode([], [], [], [], []);
                obj.Gui.ClearImages;
                delete(obj.Dataset);

                obj.Dataset = [];

                obj.Settings.SetLastImageInfo([], obj.Reporting);

                obj.SetNoDataset(true);
                obj.Gui.UpdateGuiForNewDataset([]);

            catch exc
                if MimErrors.IsErrorCancel(exc.identifier)
                    obj.Reporting.ShowMessage('MimGuiDataset:LoadingCancelled', 'User cancelled');
                else
                    obj.Reporting.ShowMessage('MimGuiDataset:ClearDatasetFailed', ['Failed to clear dataset due to error: ' exc.message]);
                end
            end
        end

        function SaveEditedResult(obj)
            obj.ModeSwitcher.SaveEditedResult();
        end

        function DeleteThisImageInfo(obj)
            obj.DeleteDatasets(obj.GetUidOfCurrentDataset());
        end

        function DeleteImageInfo(obj, uid)
            obj.DeleteDatasets(uid);
        end

        function DeleteDatasets(obj, series_uids)
            % Removes a dataset from the database and deletes its disk cache. If the dataset
            % is currently loaded then the callback from the image database will cause the
            % current dataset to be cleared.

            obj.MimMain.DeleteDatasets(series_uids);
            obj.Settings.RemoveLastPatientUid(series_uids);
            if any(strcmp(series_uids, obj.GetUidOfCurrentDataset()))
                obj.ClearDataset();
            end
        end

        function SwitchPatient(obj, patient_id)
            if ~strcmp(patient_id, obj.GuiDatasetState.CurrentPatientId)
                obj.ModeSwitcher.UpdateCurrentMode([], [], [], [], []);
                obj.Gui.SetTab(obj.AppDef.DefaultModeOnNewDataset);
                obj.Gui.ClearImages;
                obj.DeleteListeners;
                delete(obj.Dataset);
                obj.Dataset = [];
                obj.GuiDatasetState.SetPatientClearSeries(patient_id, []);
            end
        end

        function InternalLoadImages(obj, image_info_or_uid)

            % Set this to empty in case an exception is thrown before it is
            % set
            series_uid = [];

            delete_image_info = false;
            patient_id = obj.GuiDatasetState.CurrentPatientId;
            patient_visible_name = obj.GuiDatasetState.CurrentPatientVisibleName;

            try
                if isa(image_info_or_uid, 'PTKImageInfo')
                    series_uid = image_info_or_uid.ImageUid;
                    new_dataset = obj.MimMain.CreateDatasetFromInfo(image_info_or_uid);
                elseif ischar(image_info_or_uid)
                    series_uid = image_info_or_uid;
                    new_dataset = obj.MimMain.CreateDatasetFromUid(image_info_or_uid);
                else
                   new_dataset = [];
                end

                obj.ModeSwitcher.UpdateCurrentMode([], [], [], [], []);
                obj.Gui.SetTab(obj.AppDef.DefaultModeOnNewDataset);

                obj.Gui.ClearImages();
                delete(obj.Dataset);

                obj.Dataset = new_dataset;

                obj.ReplaceListeners(new_dataset);

                image_info = obj.Dataset.GetImageInfo();
                series_uid = image_info.ImageUid;
                modality = image_info.Modality;

                [preferred_context, plugin_to_use] = obj.AppDef.GetPreferredContext(modality);

                % If the modality is not CT then we load the full dataset
                load_full_data = isempty(preferred_context);

                % Attempt to obtain the region of interest
                if ~load_full_data
                    if obj.Dataset.IsContextEnabled(preferred_context)
                        try
                            new_image = obj.Dataset.GetResult(plugin_to_use);
                        catch exc
                            if MimErrors.IsErrorCancel(exc.identifier)
                                obj.Reporting.Log('LoadImages cancelled by user');
                                load_full_data = false;
                                rethrow(exc)
                            else
                                obj.Reporting.ShowMessage('MimGuiDataset:CannotGetROI', ['Unable to extract region of interest from this dataset. Error: ' exc.message]);
                                load_full_data = true;
                            end
                        end
                    else
                        load_full_data = true;
                    end
                end

                % If we couldn't obtain the ROI, we load the full dataset
                if load_full_data
                    % Force the image to be saved so that it doesn't have to be
                    % reloaded each time
                    new_image = obj.Dataset.GetResult('PTKOriginalImage', PTKContext.OriginalImage, [], true);
                end

                % The modality may not have been set if it is not contained
                % in the file metadata, but may have been set after
                % loading, in which case we update it here
                if isempty(modality) && ~isempty(new_image.Modality)
                    modality = new_image.Modality;
                    image_info.Modality = new_image.Modality;
                end

                if isfield(new_image.MetaHeader, 'PatientID')
                    patient_id = new_image.MetaHeader.PatientID;
                else
                    patient_id = series_uid;
                end

                % Update and save settings if anything has changed
                obj.Settings.SetLastImageInfo(image_info, obj.Reporting);
                obj.Settings.AddLastPatientUid(patient_id, series_uid);

                if isempty(image_info)
                    patient_visible_name = [];
                    series_name = [];
                else
                    series_info = obj.GetImageDatabase.GetSeries(image_info.ImageUid);
                    patient_info = obj.GetImageDatabase.GetPatient(series_info.PatientId);
                    patient_visible_name = patient_info.ShortVisibleName;
                    series_name = series_info.Name;
                end

                obj.GuiDatasetState.SetPatientAndSeries(patient_id, series_uid, patient_visible_name, series_name, modality);
                obj.GuiDatasetState.ClearPlugin;
                obj.Gui.SetTab(obj.AppDef.DefaultModeOnNewDataset);

                obj.Gui.UpdateGuiForNewDataset(obj.Dataset);

                % Set the image after updating the GuiState. This is necessary because setting
                % the image triggers a GUI resize, and the side panels need to be repopulated
                % first
                if load_full_data
                    obj.SetImage(new_image, PTKContext.OriginalImage);
                else
                    obj.SetImage(new_image, PTKContext.LungROI);
                end

                % Update toolbar again because setting the image will
                % change the visibility of some gui plugins
                obj.Gui.UpdateToolbar();

                obj.Gui.LoadDefaultMarkersIfRequiredWithoutProgressBar;

            catch exc
                if MimErrors.IsErrorCancel(exc.identifier)
                    obj.Reporting.ShowProgress('Cancelling load');
                    obj.ClearDataset;
                    obj.Reporting.ShowMessage('MimGuiDataset:LoadingCancelled', 'User cancelled loading');
                elseif MimErrors.IsErrorUidNotFound(exc.identifier)
                    uiwait(errordlg('This dataset is missing. It will be removed from the patient browser.', [obj.AppDef.GetName ': Cannot find dataset'], 'modal'));
                    obj.Reporting.ShowMessage('MimGuiDataset:FileNotFound', 'The original data is missing. I am removing this dataset.');
                    delete_image_info = true;
                elseif MimErrors.IsErrorFileMissing(exc.identifier)
                    uiwait(errordlg('This dataset is missing. It will be removed from the patient browser.', [obj.AppDef.GetName ': Cannot find dataset'], 'modal'));
                    obj.Reporting.ShowMessage('MimGuiDataset:FileNotFound', 'The original data is missing. I am removing this dataset.');
                    delete_image_info = true;
                elseif MimErrors.IsErrorUnknownFormat(exc.identifier)
                    uiwait(errordlg('This is not an image file or the format is not supported. It will be removed from the Patient Browser.', [obj.AppDef.GetName ': Cannot load this image'], 'modal'));
                    obj.Reporting.ShowMessage('MimGuiDataset:FormatNotSupported', 'This file format is not supported. I am removing this dataset.');
                    delete_image_info = true;
                else
                    uiwait(errordlg(exc.message, [obj.AppDef.GetName ': Cannot load dataset'], 'modal'));
                    obj.Reporting.ShowMessage('MimGuiDataset:LoadingFailed', ['Failed to load dataset due to error: ' exc.message]);
                end

                % We do this outside the catch block, in case it throws another exception
                if delete_image_info
                    try
                        % The series_uid may have been set before the
                        % exception was thrown, in which case we use this
                        % to specify which dataset to delete
                        if isempty(series_uid)
                            obj.DeleteThisImageInfo();
                        else
                            obj.DeleteImageInfo(series_uid);
                        end
                    catch exc
                        obj.Reporting.ShowMessage('MimGuiDataset:DeleteImageInfoFailed', ['Failed to delete dataset due to error: ' exc.message]);
                    end
                end
                obj.ClearDatasetKeepPatient();
            end
        end

        function RunPlugin(obj, plugin_name, context, wait_dialog)
            % Causes the GUI to run the named plugin and display the result

            if ~obj.DatasetIsLoaded()
                return;
            end

            try
                obj.RunPluginTryCatchBlock(plugin_name, context, wait_dialog);
            catch exc
                if MimErrors.IsErrorCancel(exc.identifier)
                    obj.Reporting.ShowMessage('MimGuiDataset:LoadingCancelled', ['The cancel button was clicked while the plugin ' plugin_name ' was running.']);
                else
                    obj.Reporting.ShowMessageFromException('MimGuiDataset:PluginFailed', ['The plugin ' plugin_name ' failed with the following error: ' exc.message], exc);
                    show_error_dialog = true;

                    if isa(exc, 'MimSuggestEditException')
                        default_edited_result = obj.Dataset.GetDefaultEditedResult(exc.PluginToEdit, exc.PluginContext);
                        if ~isempty(default_edited_result)
                            show_error_dialog = false;

                            choice = questdlg(['Segmentation for ' exc.PluginVisibleName ' could not be performed automatically because the algorithm was not able to process this dataset. Do you wish to create the segmentation manually using the editing tools? If you are unsure, click Cancel.'], ...
                                ['Segmentation failed for ' exc.PluginVisibleName '.'], ...
                                'Create segmentation manually', 'Cancel', 'Cancel');
                            switch choice
                                case 'Create segmentation manually'
                                    % Save the new edit
                                    obj.Dataset.SaveEditedResult(exc.PluginToEdit, default_edited_result, exc.PluginContext);

                                    % Run the plugin to load the edit
                                    % into the viewer
                                    obj.RunPluginTryCatchBlock(exc.PluginToEdit, context, wait_dialog);

                                    % Switch to edit mode
                                    obj.Gui.ChangeMode(MimModes.EditMode);

                                    uiwait(warndlg(['The segmentation created for ' exc.PluginVisibleName ' is incomplete. Please review and corect the segmentation before performing any analysis.'], ['Review and correct  ' exc.PluginVisibleName], 'modal'));
                            end
                        end

                    end

                    if show_error_dialog
                        uiwait(errordlg(['The plugin ' plugin_name ' failed with the following error: ' exc.message], [obj.AppDef.GetName ': Failure in plugin ' plugin_name], 'modal'));
                    end
                end
            end
            wait_dialog.Hide;
        end

        function InvalidateCurrentPluginResult(obj)
            % Indicates that the currently loaded result has been deleted or modified in
            % such a way that it is no longer representative of the plugin

            obj.GuiDatasetState.ClearPlugin();
            obj.UpdateModes();
        end

        function LoadManualSegmentationCallback(obj, segmentation_name)
            obj.Gui.LoadSegmentationCallback(segmentation_name);
        end

        function LoadAndDisplayManualSegmentation(obj, segmentation_name, wait_dialog)
            % Causes the GUI to run the named segmentation and display the result

            if ~obj.DatasetIsLoaded()
                return;
            end

            visible_name = segmentation_name;
            wait_dialog.ShowAndHold(['Loading segmentation ' visible_name]);

            new_image = obj.LoadManualSegmentation(segmentation_name);

            image_title = visible_name;
            image_title = ['MANUAL SEGMENTATION ', image_title];
            if isempty(new_image)
                obj.Reporting.Error('MimGuiDataset:EmptyImage', ['The segmentation ' segmentation_name ' did not return an image when expected. ']);
            end
            obj.Gui.ReplaceOverlayImageCallback(new_image, image_title);
            % We need to reset the image changed callback because we have
            % deliberately changed the image, and otherwise UpdateModes()
            % will trigger a prompt to the user to save changes
            obj.ModeSwitcher.MarkOverlayAsUnchanged();
            obj.GuiDatasetState.SetSegmentation(segmentation_name);
            obj.UpdateModes();

            wait_dialog.Hide;
        end

        function OverlayImageChanged(obj)
            obj.ModeSwitcher.OverlayImageChanged;
        end

        function UpdateModeTabControl(obj)
            obj.Gui.UpdateModeTabControl(obj.GuiDatasetState);
        end

        function SetNoDataset(obj, keep_patient)
            if keep_patient
                obj.GuiDatasetState.ClearSeries();
            else
                obj.GuiDatasetState.ClearPatientAndSeries();
            end
            obj.GuiDatasetState.ClearPlugin();
            obj.UpdateModes();
            obj.Gui.UpdateGuiForNewDataset([]);
        end

        function UpdateEditedStatus(obj, is_edited)
            obj.GuiDatasetState.UpdateEditStatus(is_edited);
        end

        function segmentation_list = GetListOfManualSegmentations(obj)
            if isempty(obj.Dataset)
                segmentation_list = CorePair.empty;
            else
                segmentation_list = obj.Dataset.GetListOfManualSegmentations();
            end
        end

        function context_list = GetAllContextsForManualSegmentations(obj)
            if isempty(obj.Dataset)
                context_list = {};
            else
                context_list = obj.Dataset.GetAllContextsForManualSegmentations();
            end
        end

        function segmentation_list = GetListOfMarkerSets(obj)
            if isempty(obj.Dataset)
                segmentation_list = CorePair.empty();
            else
                segmentation_list = obj.Dataset.GetListOfMarkerSets();
            end
        end

        function delete(obj)
            obj.DeleteListeners();
        end

        function plugin_cache = GetPluginCache(obj)
            plugin_cache = obj.MimMain.FrameworkSingleton.GetPluginInfoMemoryCache();
        end

        function preview_image = FetchPreview(obj, plugin_name)
            if isempty(obj.Dataset)
                preview_image = [];
            else
                preview_image = obj.Dataset.GetPluginPreview(plugin_name);
            end
        end

       function is_linked_dataset = IsLinkedDataset(obj, linked_name_or_uid)
            % Returns true if another dataset has been linked to this one, using
            % the name or uid specified

            if isempty(obj.Dataset)
                is_linked_dataset = false;
            else
                is_linked_dataset = obj.Dataset.IsLinkedDataset(linked_name_or_uid);
            end
       end

        function is_gas_mri = IsGasMRI(obj)
            % Check if this is a hyperpolarised gas MRI image

            if isempty(obj.Dataset)
                is_gas_mri = false;
            else
                is_gas_mri = obj.Dataset.IsGasMRI();
            end
        end

        function SaveTableAsCSV(obj, plugin_name, subfolder_name, file_name, description, table, file_dim, row_dim, col_dim, filters)
            if isempty(obj.Dataset)
                obj.Reporting.Error('MimGuiDataset:NoDatasetLoaded', ['Could not save analysis results because no dataset is currently loaded.']);
            else
                obj.Dataset.SaveTableAsCSV(plugin_name, subfolder_name, file_name, description, table, file_dim, row_dim, col_dim, filters);
            end
        end

        function ViewerPanelModeChanged(obj, new_mode)
            % This methods is called when the current mode in the viewer panel has changed

            obj.ModeSwitcher.ViewerPanelModeChanged(new_mode, obj.Dataset, obj.GuiDatasetState.CurrentPluginInfo, obj.GuiDatasetState.CurrentPluginName, obj.GuiDatasetState.CurrentVisiblePluginName, obj.CurrentContext);
        end
    end

    methods (Access = private)

        function DeleteListeners(obj)
            CoreSystemUtilities.DeleteIfValidObject(obj.PreviewImageChangedListener);
            CoreSystemUtilities.DeleteIfValidObject(obj.MarkersChangedListener);
            CoreSystemUtilities.DeleteIfValidObject(obj.ManualSegmentationsChangedListener);
            obj.PreviewImageChangedListener = [];
        end

        function ReplaceListeners(obj, new_dataset)
            obj.DeleteListeners();
            if ~isempty(new_dataset)
                obj.PreviewImageChangedListener = addlistener(new_dataset, 'PreviewImageChanged', @obj.PreviewImageChangedCallback);
                obj.MarkersChangedListener = addlistener(new_dataset, 'MarkersChanged', @obj.MarkersChangedCallback);
                obj.ManualSegmentationsChangedListener = addlistener(new_dataset, 'ManualSegmentationsChanged', @obj.ManualSegmentationsChangedCallback);
            end
        end

        function SeriesHasBeenDeleted(obj, series_uid, ~)
            % If the currently loaded dataset has been removed from the database, then clear
            % and delete
            if strcmp(series_uid, obj.GetUidOfCurrentDataset)
                obj.ClearDataset;
            end
        end

        function ModeHasChanged(obj, ~, mode)
            % Called when the mode changes
            obj.Gui.SetTabMode(mode.Data);
        end

        function result = RunPluginTryCatchBlock(obj, plugin_name, context_to_request, wait_dialog)
            new_plugin = obj.LoadPluginInfoStructure(plugin_name, obj.Reporting);
            visible_name = CoreTextUtilities.RemoveHtml(new_plugin.ButtonText);
            wait_dialog.ShowAndHold(['Computing ' visible_name]);

            if strcmp(new_plugin.PluginType, 'DoNothing')
                % Call with 2 output arguments to prevent fetching of cache
                % info
                [result, ~] = obj.Dataset.GetResultWithCacheInfo(plugin_name, context_to_request);
            else

                % Determine the context we require (full image, lung ROI,
                % etc) if not specified by the caller
                % Normally we keep the last context, but if a context plugin is
                % selected, we switch to the new context
                if isempty(context_to_request)
                    context_to_request = obj.CurrentContext;
                end
                if strcmp(new_plugin.PluginType, 'ReplaceImage')
                    context_to_request = obj.ContextDef.ChooseOutputContext(new_plugin.Context);
                end

                [result, cache_info, new_image] = obj.Dataset.GetResultWithCacheInfo(plugin_name, context_to_request);

                if isa(cache_info, 'MimCompositeResult')
                    cache_info = cache_info.GetFirstResult;
                end

                image_title = visible_name;
                if cache_info.IsEdited
                    image_title = ['EDITED ', image_title];
                end
                if strcmp(new_plugin.PluginType, 'ReplaceOverlay')
                    if isempty(new_image)
                        obj.Reporting.Error('MimGuiDataset:EmptyImage', ['The plugin ' plugin_name ' did not return an image when expected. If this plugin should not return an image, then set its PluginType property to "DoNothing"']);
                    end
                    obj.ModeSwitcher.PrePluginCall();
                    obj.Gui.ReplaceOverlayImageCallback(new_image, image_title);
                    obj.GuiDatasetState.SetPlugin(new_plugin, plugin_name, visible_name, cache_info.IsEdited);
                    obj.UpdateModes();

                elseif strcmp(new_plugin.PluginType, 'ReplaceQuiver')

                    obj.Gui.ReplaceQuiverCallback(new_image);

                elseif strcmp(new_plugin.PluginType, 'ReplaceImage')
                    obj.SetImage(new_image, context_to_request);
                end
            end
        end

        function PreviewImageChangedCallback(obj, ~, event_data)
            notify(obj.GuiDatasetState, 'PreviewImageChanged', CoreEventData(event_data.Data));
        end

        function ManualSegmentationsChangedCallback(obj, ~, event_data)
            notify(obj.GuiDatasetState, 'ManualSegmentationsChanged', CoreEventData(event_data.Data));
        end

        function MarkersChangedCallback(obj, ~, event_data)
            notify(obj.GuiDatasetState, 'MarkersChanged', CoreEventData(event_data.Data));
        end

        function SetImage(obj, new_image, context)
            obj.CurrentContext = context;
            obj.Gui.SetImage(new_image);
        end

        function UpdateModes(obj)
            obj.ModeSwitcher.UpdateCurrentMode(obj.Dataset, obj.GuiDatasetState.CurrentPluginInfo, obj.GuiDatasetState.CurrentPluginName, obj.GuiDatasetState.CurrentVisiblePluginName, obj.CurrentContext);
            obj.Gui.UpdateModeTabControl(obj.GuiDatasetState);
            obj.Gui.UpdateToolbar();
        end

        function new_plugin = LoadPluginInfoStructure(obj, plugin_name, reporting)
            % Obtains a handle to the plugin which can be used to parse its properties
            new_plugin = obj.GetPluginCache.GetPluginInfo(plugin_name, [], reporting);
        end
    end
end


function md5=mocov_util_md5(filename)
% return the md5sum from the contents of a file
%
% md5=mocov_util_md5(filename)
%
% Inputs:
%   filename            name of file for which md5 must be computed
%
% Output:
%   md5                 md5 checksum of the contents of the file filename
%
% Notes:
%    - this function requires either the presence of a function named
%      'md5sum' (available on the GNU Octave platform), or the usage of a
%      unix-like platform
%
%
    md5_with_whitespace=md5_from_file(filename);
    md5=regexprep(md5_with_whitespace,'\s','');%

function md5=md5_from_file(fn)
    md5_processors={@md5_builtin,...
                    @hash_builtin,...
                    @md5sum_shell,...
                    @md5_shell};

    n=numel(md5_processors);
    for k=1:n
        md5_processor=md5_processors{k};
        [is_ok,md5]=md5_processor(fn);
        if is_ok
            return;
        end
    end

    error('Unable to compute md5 - no method available');

function [is_ok,md5]=md5_builtin(fn)
% supported in GNU Octave <= 4.6
    is_ok=has_builtin_function('md5sum');
    if is_ok
        md5=md5sum(fn);
    else
        md5=[];
    end

function [is_ok,md5]=hash_builtin(fn)
% supported in GNU Octave >= 4.4

    is_ok=has_builtin_function('hash') && has_builtin_function('fileread');
    if is_ok
        md5=hash('md5',fileread(fn));
    else
        md5=[];
    end

function tf=has_builtin_function(name)
    tf=exist(name,'builtin');

function [is_ok,md5]=run_unix(cmd)
% helper function
    is_ok=false;
    md5=[];

    if ispc()
        return;
    end

    [status,md5]=unix(cmd);
    is_ok=status==0;

function [is_ok,md5]=md5sum_shell(fn)
% supported on Unix platform

    cmd=sprintf('md5sum "%s"',fn);
    [is_ok,md5_with_fn]=run_unix(cmd);
    parts=regexp(md5_with_fn,'\s+','split');
    md5=parts{1};

function [is_ok,md5]=md5_shell(fn)
% supported on Unix platform

    cmd=sprintf('md5 -q "%s"',fn);
    [is_ok,md5]=run_unix(cmd);


% NOTE: DO NOT REMOVE THIS LINE XMAKEFILE_TOOL_CHAIN_CONFIGURATION
function toolChainConfiguration = arduinomega2560()
%ARDUINO Defines a tool chain configuration

% Copyright 2012 The MathWorks, Inc.

try
    hSetup = realtime.setup.PackageInfo;
    spPkg = hSetup.getSpPkgInfo('Arduino Mega 2560');
    arduinoRootDir = hSetup.getTpPkgRootDir('ARDUINO', spPkg);
catch me
    toolChainConfiguration = {};
    return;
end

% General
toolChainConfiguration.Configuration = 'Arduinomega2560';
toolChainConfiguration.Version = '2.0';
toolChainConfiguration.Description = 'MS Visual Studio';
toolChainConfiguration.Operational = true;
toolChainConfiguration.Decorator = 'linkfoundation.xmakefile.decorator.eclipseDecorator';
% Make
toolChainConfiguration.MakePath = @(src) fullfile(arduinoRootDir, 'hardware', 'tools', 'avr', 'utils', 'bin', 'make');
toolChainConfiguration.MakeFlags = '-f "[|||MW_XMK_GENERATED_FILE_NAME[R]|||]" [|||MW_XMK_ACTIVE_BUILD_ACTION_REF|||]';
toolChainConfiguration.MakeInclude = '';
% Compiler
toolChainConfiguration.CompilerPath = @(src) fullfile(arduinoRootDir, 'hardware', 'tools', 'avr', 'bin', 'avr-gcc');
toolChainConfiguration.CompilerFlags = '-c -x none';
toolChainConfiguration.SourceExtensions = '.c,.cpp';
toolChainConfiguration.HeaderExtensions = '.h';
toolChainConfiguration.ObjectExtension = '.o';
% Linker
toolChainConfiguration.LinkerPath = @(src) fullfile(arduinoRootDir, 'hardware', 'tools', 'avr', 'bin', 'avr-gcc');
toolChainConfiguration.LinkerFlags = '-o [|||MW_XMK_GENERATED_TARGET_REF|||]';
toolChainConfiguration.LibraryExtensions = '.lib,.a';
toolChainConfiguration.TargetExtension = '.elf';
toolChainConfiguration.TargetNamePrefix = '';
toolChainConfiguration.TargetNamePostfix = '';
% Archiver
toolChainConfiguration.ArchiverPath = @(src) fullfile(arduinoRootDir, 'hardware', 'tools', 'avr', 'bin', 'avr-ar');
toolChainConfiguration.ArchiverFlags = '-crs $(TARGET_FILE)';
toolChainConfiguration.ArchiveExtension = '.a';
toolChainConfiguration.ArchiveNamePrefix = 'lib';
toolChainConfiguration.ArchiveNamePostfix = '';
% Pre-build
toolChainConfiguration.PrebuildEnable = false;
toolChainConfiguration.PrebuildToolPath = '';
toolChainConfiguration.PrebuildFlags = '';
% Post-build
toolChainConfiguration.PostbuildEnable = true;
toolChainConfiguration.PostbuildToolPath = @(src) fullfile(arduinoRootDir, 'hardware', 'tools', 'avr', 'bin', 'avr-objcopy');
toolChainConfiguration.PostbuildFlags = '-O ihex -R .eeprom $(TARGET)  [|||MW_XMK_MODEL_NAME|||].hex';
% Execute
toolChainConfiguration.ExecuteDefault = false;
toolChainConfiguration.ExecuteToolPath = '';
toolChainConfiguration.ExecuteFlags = '';
% Directories
toolChainConfiguration.DerivedPath = '[|||MW_XMK_SOURCE_PATH_REF|||]';
toolChainConfiguration.OutputPath = '';
% Custom
toolChainConfiguration.Custom1 = '';
toolChainConfiguration.Custom2 = '';
toolChainConfiguration.Custom3 = '';
toolChainConfiguration.Custom4 = '';
toolChainConfiguration.Custom5 = '';
end


function varargout = showufmf(varargin)
% showufmf
%
% Opens a GUI to show UFMF-compressed video
% Optional inputs:
%
% UFMFName: Name of input video.
%
%

% Edit the above text to modify the response to help showufmf

% Last Modified by GUIDE v2.5 18-Jul-2014 11:42:42

% Begin initialization code - DO NOT EDIT
gui_Singleton = 0;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @showufmf_OpeningFcn, ...
                   'gui_OutputFcn',  @showufmf_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1}) && exist(varargin{1},'file'),
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before showufmf is made visible.
function showufmf_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to showufmf (see VARARGIN)

% Choose default command line output for showufmf
handles.output = hObject;

handles.MAXSLIDERRES = 10^6;

handles.figpos = get(handles.figure1,'Position');

% set up path
if isempty(which('myparse')),
  if exist('../misc','file'),
    addpath('../misc');
  end
  while isempty(which('myparse')),
    miscdir = uigetdir('.','Choose "misc" folder to add to path');
    try
      addpath(miscdir);
    catch %#ok<CTCH>
    end
  end
end

% load previous values
handles.rcfilename = '.showufmfrc.mat';
handles.previous_values = struct;
if exist(handles.rcfilename,'file')
  handles.previous_values = load(handles.rcfilename);
end

if isfield(handles.previous_values,'filename'),
  handles.filename = handles.previous_values.filename;
  if ~exist(handles.filename,'file'),
    handles.filename = '';
  end
else
  handles.filename = '';
end

handles.menu_Colormaps = get(handles.menu_Colormap,'Children');
if isfield(handles.previous_values,'Colormap'),
  handles.Colormap = handles.previous_values.Colormap;
else
  handles.Colormap = 'gray';
end

for h = handles.menu_Colormaps',
  set(h,'Checked','off');
end
switch handles.Colormap,
  case 'gray',
    set(handles.menu_ColormapGray,'Checked','on');
  case 'jet',
    set(handles.menu_ColormapJet,'Checked','on');
end

if isfield(handles.previous_values,'MaxFPS'),
  handles.MaxFPS = handles.previous_values.MaxFPS;
else
  handles.MaxFPS = 0;
  handles.MinSPF = 0;
end
if isfield(handles.previous_values,'BackSubThresh'),
  handles.BackSubThresh = handles.previous_values.BackSubThresh;
else
  handles.BackSubThresh = 10;
end

[handles.filename,handles.MaxFPS,handles.FMFName,handles.BackSubThresh,figpos,handles.FirstFrame] = myparse(varargin,...
  'UFMFName',handles.filename,'MaxFPS',handles.MaxFPS,'FMFName','','BackSubThresh',handles.BackSubThresh,...
  'FigPos',[],'FirstFrame',1);
if ~isempty(figpos),
  % nan means auto-set
  figpos(isnan(figpos)) = handles.figpos(isnan(figpos));
  handles.figpos = figpos;
  set(handles.figure1,'Units','Pixels','Position',figpos);
end
if handles.MaxFPS == 0,
  handles.MinSPF = 0;
else
  handles.MinSPF = 1/handles.MaxFPS;
end
handles.UFMFNameIsInput = ismember('UFMFName',varargin(1:2:end));
handles.FMFNameIsInput = ~isempty(handles.FMFName) && exist(handles.FMFName,'file');

s1 = {'Compressed Frame'
  'Background Model'
  'Background Difference'
  'Is Foreground'};
s2 = {'Raw Frame'
  'Compression Error'
  'Thresholded Compression Error'};
if handles.FMFNameIsInput,
  set(handles.popupmenu_Show,'String',[s1;s2]);
else
  set(handles.popupmenu_Show,'String',s1);
end

% set slider listener
fcn = get(handles.slider_Frame,'Callback');
if verLessThan('matlab','8.4.0')
  handles.hslider_listener = handle.listener(handles.slider_Frame,...
    'ActionEvent',fcn);
  set(handles.slider_Frame,'Callback','');
else
  handles.hslider_listener = addlistener(handles.slider_Frame,...
    'ContinuousValueChange',fcn);
  set(handles.slider_Frame,'Callback','');
end

%handles.slider_listener = handle.listener(handles.slider_Frame,'ActionEvent',fcn);

% open video
handles = open_fmf(handles);

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes showufmf wait for user response (see UIRESUME)
% uiwait(handles.figure1);

function play(hObject)

handles = guidata(hObject);
set(hObject,'String','Stop','BackgroundColor',[.5,0,0]);
handles.isplaying = true;
guidata(hObject,handles);
tic;
t0 = now;
fnext = handles.f;

while true,
  handles = guidata(hObject);
  if ~handles.isplaying,
    break;
  end
  f = fnext;
  handles.f = f;
  handles = update_frame(handles);
  guidata(hObject,handles);
  if handles.MaxFPS > 0,
    tmp = toc;
    t1 = now;
    if tmp < handles.MinSPF
      pause(handles.MinSPF - t1);
      fnext = f + 1;
    elseif tmp > handles.MinSPF,
      fnext = max(1,round((t1-t0)*24*3600/handles.MinSPF));
      %fprintf('setting fnext to %d\n',fnext);
      if fnext > handles.nframes,
        break;
    end
      drawnow;
  else
    drawnow;
  end
  else
    fnext = f+1;
    drawnow;
  end

%   if handles.MaxFPS > 0,
%     tmp = toc;
%     if tmp < handles.MinSPF
%       pause(handles.MinSPF - tmp);
%     end
%   else
%     drawnow;
%   end
  tic;
end

stop(hObject);

function stop(hObject)

handles = guidata(hObject);
handles.isplaying = false;
set(hObject,'String','Play','BackgroundColor',[0,.5,0]);
guidata(hObject,handles);

% --- Outputs from this function are returned to the command line.
function varargout = showufmf_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;

function handles = update_frame(handles)

try
  [handles.im,handles.timestamp,handles.cachedidx,handles.bb,handles.mu] = handles.readframe(handles.f);
catch ME
  warning([sprintf('Error reading frame %d\n',handles.f),getReport(ME)]);
  return;
end
s = get(handles.popupmenu_Show,'String');
v = get(handles.popupmenu_Show,'Value');
showType = s{v};
switch showType,
  case 'Compressed Frame',
    set(handles.himage,'CData',handles.im);
    set(handles.axes_Video,'CLim',[0,255]);
  case 'Background Model',
    set(handles.himage,'CData',handles.mu);
    set(handles.axes_Video,'CLim',[0,255]);
  case 'Background Difference',
    if handles.FMFNameIsInput,
      im = handles.readframe_raw(handles.f);
      if size(im,3) > 1,
        im = rgb2gray(im);
      end
      im = cast(im,handles.headerinfo.dataclass);
    else
      im = handles.im;
    end
    diff = max(im - handles.mu,handles.mu - im);
    set(handles.himage,'CData',diff);
    set(handles.axes_Video,'CLim',[0,max(1,max(diff(:)))]);
  case 'Is Foreground',
    if handles.FMFNameIsInput,
      im = handles.readframe_raw(handles.f);
      if size(im,3) > 1,
        im = rgb2gray(im);
      end
      im = cast(im,handles.headerinfo.dataclass);
    else
      im = handles.im;
    end
    if size(im,3) > 1,
      im = rgb2gray(im);
    end
    diff = max(im - handles.mu,handles.mu - im);
    isfore = diff >= handles.BackSubThresh;
    set(handles.himage,'CData',isfore);
    set(handles.axes_Video,'CLim',[0,1]);
  case 'Raw Frame',
    im = handles.readframe_raw(handles.f);
    if size(im,3) > 1,
      im = rgb2gray(im);
    end
    im = cast(im,handles.headerinfo.dataclass);
    handles.rawim = im;
    set(handles.himage,'CData',handles.rawim);
    set(handles.axes_Video,'CLim',[0,255]);
  case 'Compression Error',
    im = handles.readframe_raw(handles.f);
    if size(im,3) > 1,
      im = rgb2gray(im);
    end
    im = cast(im,handles.headerinfo.dataclass);
    handles.rawim = im;
    diff = max(handles.im - handles.rawim,handles.rawim - handles.im);
    set(handles.himage,'CData',diff);
    set(handles.axes_Video,'CLim',[0,max(1,max(diff(:)))]);
  case 'Thresholded Compression Error',
    im = handles.readframe_raw(handles.f);
    if size(im,3) > 1,
      im = rgb2gray(im);
    end
    im = cast(im,handles.headerinfo.dataclass);
    handles.rawim = im;
    diff = max(handles.im - handles.rawim,handles.rawim - handles.im);
    isfore = diff >= handles.BackSubThresh;
    set(handles.himage,'CData',isfore);
    set(handles.axes_Video,'CLim',[0,1]);
end
if(handles.headerinfo.is_fixed_size)
  y = cat(2,handles.bb(:,1),...
    handles.bb(:,1),...
    handles.bb(:,1)+handles.headerinfo.max_width,...
    handles.bb(:,1)+handles.headerinfo.max_width,...
    handles.bb(:,1),...
    nan(size(handles.bb,1),1))'-.5;
  x = cat(2,handles.bb(:,2),...
    handles.bb(:,2)+handles.headerinfo.max_height,...
    handles.bb(:,2)+handles.headerinfo.max_height,...
    handles.bb(:,2),...
    handles.bb(:,2),...
    nan(size(handles.bb,1),1))'-.5;
else
  y = cat(2,handles.bb(:,1),...
    handles.bb(:,1),...
    handles.bb(:,1)+handles.bb(:,3),...
    handles.bb(:,1)+handles.bb(:,3),...
    handles.bb(:,1),...
    nan(size(handles.bb,1),1))'-.5;
  x = cat(2,handles.bb(:,2),...
    handles.bb(:,2)+handles.bb(:,4),...
    handles.bb(:,2)+handles.bb(:,4),...
    handles.bb(:,2),...
    handles.bb(:,2),...
    nan(size(handles.bb,1),1))'-.5;
end
set(handles.hboxes,'xdata',x(:),'ydata',y(:));
set(handles.edit_Frame,'String',num2str(handles.f));
set(handles.slider_Frame,'Value',handles.f);

% --- Executes on slider movement.
function slider_Frame_Callback(hObject, eventdata, handles)
% hObject    handle to slider_Frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
v = get(hObject,'Value');
roundv = round(v);
if roundv == handles.f,
  if v > handles.f,
    handles.f = min(handles.nframes,handles.f+1);
  elseif v < handles.f,
    handles.f = max(1,handles.f-1);
  end
else
  handles.f = roundv;
end

handles = update_frame(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function slider_Frame_CreateFcn(hObject, eventdata, handles)
% hObject    handle to slider_Frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

% --- Executes on button press in pushbutton_PlayStop.
function pushbutton_PlayStop_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_PlayStop (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if ~handles.isplaying,
  play(hObject);
else
  stop(hObject);
end

function edit_Frame_Callback(hObject, eventdata, handles)
% hObject    handle to edit_Frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_Frame as text
%        str2double(get(hObject,'String')) returns contents of edit_Frame as a double
f = str2double(get(hObject,'String'));
if isnan(f),
  set(hObject,'String',num2str(handles.f));
  return;
end
handles.f = max(1,min(f,handles.nframes));
handles = update_frame(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_Frame_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_Frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --------------------------------------------------------------------
function menu_File_Callback(hObject, eventdata, handles)
% hObject    handle to menu_File (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function menu_File_Open_Callback(hObject, eventdata, handles)
% hObject    handle to menu_File_Open (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

handles = open_fmf(handles);
guidata(hObject,handles);

function handles = open_fmf(handles)

if isfield(handles,'isplaying') && handles.isplaying,
  stop(handles.figure1);
  handles = guidata(handles.figure1);
end

if ~handles.UFMFNameIsInput,
  handles.filterspec = {'*.ufmf','MicroFlyMovieFormat (*.ufmf)'};

  [filename, pathname] = uigetfile(handles.filterspec, 'Choose UFMF video to play',handles.filename);
  if ~ischar(filename),
    return;
  end
  handles.filename = fullfile(pathname,filename);
else
  handles.UFMFNameIsInput = false;
end

if isfield(handles,'fid') && ~isempty(fopen(handles.fid)),
  fclose(handles.fid);
end
if isfield(handles,'fid_raw') && ~isempty(fopen(handles.fid_raw)),
  fclose(handles.fid_raw);
end
if isfield(handles,'himage') && ishandle(handles.himage),
  delete(handles.himage);
end

try
  [handles.readframe,handles.nframes,handles.fid,handles.headerinfo] = ...
    get_readframe_fcn(handles.filename);
catch ME
  s = sprintf('Could not read video %s.',handles.filename);
  uiwait(errordlg(s,'Error opening video'));
  rethrow(ME);
end

if handles.FMFNameIsInput,
  [handles.readframe_raw,handles.nframes_raw,handles.fid_raw,handles.headerinfo_raw] = ...
    get_readframe_fcn(handles.FMFName);
end

% set slider steps
% this seems to be the limit to slider step resolution
handles.stepsize = ceil(handles.nframes/handles.MAXSLIDERRES);
step1 = handles.stepsize/(handles.nframes-1);
sliderstep = [step1,min(1,100*step1)];
set(handles.slider_Frame,'Value',0,'SliderStep',sliderstep,'Min',1,'Max',handles.nframes);

% show first image
if ischar(handles.FirstFrame) && strcmpi(handles.FirstFrame,'maxnboxes'),
  nboxes = ufmf_read_nboxes(handles.headerinfo,1:handles.nframes);
  [~,handles.FirstFrame] = max(nboxes);
end
handles.f = handles.FirstFrame;
[handles.im,handles.timestamp,handles.cachedidx,handles.bb,handles.mu] = handles.readframe(handles.f);
hold(handles.axes_Video,'off');
if size(handles.im,3) == 1,
  handles.himage = imagesc(handles.im,'Parent',handles.axes_Video,[0,255]);
else
  handles.himage = image(uint8(handles.im),'Parent',handles.axes_Video);
end
colormap(handles.axes_Video,handles.Colormap);
axis(handles.axes_Video,'image','off');
axis(handles.axes_Video,[0,size(handles.im,2)+1,0,size(handles.im,1)+1]);
hold(handles.axes_Video,'on');
handles.hboxes = plot(handles.axes_Video,nan(1,2),nan(1,2),'r-');

handles = update_frame(handles);

handles.isplaying = false;

% --------------------------------------------------------------------
function menu_File_Quit_Callback(hObject, eventdata, handles)
% hObject    handle to menu_File_Quit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
figure1_CloseRequestFcn(handles.figure1, eventdata, handles);

% --- Executes when user attempts to close figure1.
function figure1_CloseRequestFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: delete(hObject) closes the figure
if isfield(handles,'fid') && ~isempty(fopen(handles.fid)),
  fclose(handles.fid);
end
if isfield(handles,'fid_raw') && ~isempty(fopen(handles.fid_raw)),
  fclose(handles.fid_raw);
end
savefns = {'filename','FMFName','MaxFPS','BackSubThresh','Colormap'};
save(handles.rcfilename,'-struct','handles',savefns{:});
delete(hObject);

% --- Executes when figure1 is resized.
function figure1_ResizeFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if ~isfield(handles,'figpos'),
  return;
end

sliderpos = get(handles.slider_Frame,'Position');
editpos = get(handles.edit_Frame,'Position');
pushbuttonpos = get(handles.pushbutton_PlayStop,'Position');
axespos = get(handles.axes_Video,'Position');
newfigpos = get(handles.figure1,'Position');
popupmenupos = get(handles.popupmenu_Show,'Position');

woff = newfigpos(3) - handles.figpos(3);
hoff = newfigpos(4) - handles.figpos(4);

sliderpos(3) = sliderpos(3) + woff;
editpos(1) = editpos(1) + woff;
pushbuttonpos(1) = pushbuttonpos(1) + woff;
popupmenupos(1) = popupmenupos(1) + woff;

axespos(3) = axespos(3) + woff;
axespos(4) = axespos(4) + hoff;

set(handles.slider_Frame,'Position',sliderpos);
set(handles.edit_Frame,'Position',editpos);
set(handles.pushbutton_PlayStop,'Position',pushbuttonpos);
set(handles.axes_Video,'Position',axespos);
set(handles.popupmenu_Show,'Position',popupmenupos);

handles.figpos = newfigpos;
guidata(hObject,handles);

% --------------------------------------------------------------------
function menu_Edit_Preferences_Callback(hObject, eventdata, handles)
% hObject    handle to menu_Edit_Preferences (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

MAXFPS_IDX = 1;
BACKSUBTHRESH_IDX = 2;
prompts = {};
defAns = {};
prompts{MAXFPS_IDX} = 'Max FPS: ';
defAns{MAXFPS_IDX} = num2str(handles.MaxFPS);
prompts{BACKSUBTHRESH_IDX} = 'BackSub Thresh: ';
defAns{BACKSUBTHRESH_IDX} = num2str(handles.BackSubThresh);
while true,
  answer = inputdlg(prompts,'showufmf Preferences',1,defAns,'on');
  if isempty(answer),
    return;
  end
  v = str2double(answer{MAXFPS_IDX});
  iserror = false;
  if isnan(v),
    iserror = true;
  else
    defAns{MAXFPS_IDX} = num2str(v);
    handles.MaxFPS = v;
    handles.MinSPF = 1 / handles.MaxFPS;
  end
  v = str2double(answer{BACKSUBTHRESH_IDX});
  if isnan(v),
    iserror = true;
  else
    defAns{BACKSUBTHRESH_IDX} = num2str(v);
    handles.BackSubThresh = v;
  end

  if iserror,
    uiwait(warndlg('Illegal values entered. Max FPS and BackSub Thresh must be numbers','Bad Preferences'));
  else
    break;
  end
end
guidata(hObject,handles);

% --------------------------------------------------------------------
function menu_Help_About_Callback(hObject, eventdata, handles)
% hObject    handle to menu_Help_About (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

s = {};
s{end+1} = 'ShowUFMF';
s{end+1} = '';
s{end+1} = 'Kristin Branson';
s{end+1} = 'bransonk@janelia.hhmi.org';
s{end+1} = '';
s{end+1} = 'This is a GUI for showing UFMF compression results. The maximum frame rate can be set through the "Preferences..." menu. Set to <= 0 for no maximum frame rate. Control the frame shown with the slider or editable text box. The Play/Stop button does what you think it does.';
msgbox(s,'About ShowUFMF','help','Replace');

% --------------------------------------------------------------------
function menu_Edit_Callback(hObject, eventdata, handles)
% hObject    handle to menu_Edit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function menu_Help_Callback(hObject, eventdata, handles)
% hObject    handle to menu_Help (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --- Executes on selection change in popupmenu_Show.
function popupmenu_Show_Callback(hObject, eventdata, handles)
% hObject    handle to popupmenu_Show (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_Show contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupmenu_Show
handles = update_frame(handles);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function popupmenu_Show_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupmenu_Show (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

% --------------------------------------------------------------------
function menu_ShowForegroundBoxes_Callback(hObject, eventdata, handles)
% hObject    handle to menu_ShowForegroundBoxes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

v = get(hObject,'Checked');
if strcmpi(v,'on'),
  v = 'off';
else
  v = 'on';
end
set(hObject,'Checked',v);

if isfield(handles,'hboxes'),
  set(handles.hboxes,'Visible',v);
end

% --------------------------------------------------------------------
function menu_Colormap_Callback(hObject, eventdata, handles)
% hObject    handle to menu_Colormap (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function menu_ColormapGray_Callback(hObject, eventdata, handles)
% hObject    handle to menu_ColormapGray (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if strcmpi(get(hObject,'Checked'),'off'),
  for h = handles.menu_Colormaps',
    if h ~= hObject,
      set(h,'Checked','off');
    end
  end
  set(hObject,'Checked','on');
  set(handles.hboxes,'color','r');
  handles.Colormap = 'gray';
  colormap(handles.axes_Video,handles.Colormap);
  guidata(hObject,handles);
end

% --------------------------------------------------------------------
function menu_ColormapJet_Callback(hObject, eventdata, handles)
% hObject    handle to menu_ColormapJet (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if strcmpi(get(hObject,'Checked'),'off'),
  for h = handles.menu_Colormaps',
    if h ~= hObject,
      set(h,'Checked','off');
    end
  end
  set(hObject,'Checked','on');
  set(handles.hboxes,'color','k');
  handles.Colormap = 'jet';
  colormap(handles.axes_Video,handles.Colormap);
  guidata(hObject,handles);
end

% --- Executes on key press with focus on figure1 or any of its controls.
function figure1_WindowKeyPressFcn(hObject, eventdata, handles)
% hObject    handle to figure1 (see GCBO)
% eventdata  structure with the following fields (see FIGURE)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)

switch eventdata.Key,
  case {'leftarrow','downarrow'},
    handles.f = max(1,handles.f-1);
    handles = update_frame(handles);
    guidata(hObject,handles);
  case {'rightarrow','uparrow'},
    handles.f = min(handles.nframes,handles.f+1);
    handles = update_frame(handles);
    guidata(hObject,handles);
end


classdef MimStorageFormat
    % MimStorageFormat. Describes how a cached file will be stored
    %
    %   When storing data in the cache, this enumeration can be used to
    %   choose the file format
    %
    %    Mat           - Store as a Matlab .mat file
    %    Xml           - Convert to an XML file using a structured format
    %                    that can be loaded back into the original data
    %                    using CoreLoadXml(). Use this format when you wat
    %    SimplifiedXml - Save using a more compact XML format that uses
    %                    less disk space and is easier for people to parse,
    %                    but loading the data from the simplified XML file
    %                    will not necessarily produce exactly the same
    %                    structure that was saved
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    enumeration
        Mat           % MATLAB .MAT file
        Xml           % XML file that can be automatically reloaded
        XmlSimplified % XML file that may require conversion when loading
    end
end


function [logStruct] = getLogInfo(logFilePath,varargin)
	% Opens a log file (e.g. Inscopix recording file) and outputs a structure containing the field information for the structure.
	% Biafra Ahanonu
	% started: 2014.01.30
	% based on R code by Biafra Ahanonu started: 2013.09.18
	% inputs
		%
	% outputs
		%

	[logStruct] = ciapkg.io.getLogInfo(logFilePath,'passArgs', varargin);
end


classdef FieldPrinter < handle

    properties (Access = protected)
        fileID
        fieldValues
        fieldName
        iter
        fieldPosition
        simulationStr
        fieldRepresenter
    end

    properties (Access = protected, Abstract)
       fieldType
    end

    methods (Access = protected)

        function printValuesLine(obj)
            iD = obj.fileID;
            fprintf(iD,'Values\n');
        end

        function printEndValuesLine(obj)
            iD = obj.fileID;
            fprintf(iD,'End Values\n');
        end

        function printFieldLines(obj)
            obj.fieldRepresenter.printFieldLines()
        end

        function printResultsLineHeader(obj)
           obj.fieldRepresenter.printResultsLineHeader()
        end

    end

 end


function D = addoccludedtag(D, words)
%
% Adds the field object.crop (it also considers an occluded object as a
% cropped object).
%
% Removes the words 'occluded' from the object name.

if nargin < 2
    words = {'occluded'};
end

Nwords = length(words);
changes = 0;

for i = 1:length(D)
    if isfield(D(i).annotation, 'object')
        Nobjects = length(D(i).annotation.object);
        for j = 1:Nobjects

%             if isfield(D(i).annotation.object(j), 'occluded')
%                 D(i).annotation.object(j).occluded == 'yes'
%             end

            w = lower(getwords(D(i).annotation.object(j).name));
            k = ismember(w, words);

            if sum(k)>0
                D(i).annotation.object(j).occluded = 'yes';
                changes = changes + 1;
                % remove words
                for n = 1:Nwords
                    D(i).annotation.object(j).name = strrep(D(i).annotation.object(j).name, words{n}, '');
                end
            end
        end
    end
end

% Make sure there are no new empty object names
D = LMvalidobjects(D);

fprintf('occluded field: %d changes to ''yes''\n', changes')


function answer = strend (str1, str2)
    case_sensitive = true;
    answer = strendi (str1, str2, case_sensitive);
end

%!test
%! myassert(strend('private', 'haha/private'));
%! myassert(~strend('Private', 'haha/private'));
%! myassert(~strend('private', 'haha/private2'));
%! myassert(~strend('packed:rcond:complexNotSupported', ...
%!     'packed:chol:complexNotSupported'));
%! out = strend('private', {'haha/private', 'haha/wrong', ''});
%! myassert(out, [true, false, false])


if ~isempty(strfind(fsub,'.mat'))
    if exist(fsub,'file')
        save(fsub,'X','-append')
    else
        save(fsub,'X')
    end
elseif ~isempty(strfind(fsub,'.txt'))
    dlmwrite(fsub,X);
else % no extension - assumed to be an SPM file
    error('For now, we can only deal with .mat and .txt files')
end


%function h = jim(varargin) - alias to "im" for julia compatibility
 function h = jim(varargin)
   out = im(varargin{:});
if nargout
   h = out;
end
end


function tf = regexpmatch(s,pat,varargin)
% tf = regexpmatch(s,pat,p1,v1,...)
% Either s xor pat is expected to be a cellstr.
% Optional PVs:
% - caseinsens, logical scalar

assert(iscellstr(s)&&ischar(pat) || ischar(s)&&iscellstr(pat),...
  'Invalid input arguments.');

caseinsens = myparse(varargin,'caseinsens',false);
if caseinsens
  fcn = @regexpi;
else
  fcn = @regexp;
end

if iscellstr(s)
  tf = cellfun(@(x)~isempty(fcn(x,pat,'once')),s);
else
  tf = cellfun(@(x)~isempty(fcn(s,x,'once')),pat);
end


function restorefig(h,old)
%RESTOREFIG  Restore a figure's properties
%   RESTOREFIG(H,OLD) restores the properties of H specified in
%   OLD. The state-difference structure OLD is the output of the
%   function APPLYTOFIG. Deleting object handles after the call to
%   APPLYFIG might cause undesired behavior.
%
%   See also EXPORTFIG, PREVIEWFIG, APPLYTOFIG.

%  Copyright 2000-2009 The MathWorks, Inc.

for n=1:length(old.objs)
  try
    if ~iscell(old.values{n}) & iscell(old.prop{n})
      old.values{n} = {old.values{n}};
    end
    set(old.objs{n}, old.prop{n}, old.values{n});
  end
end


%MLDIVIDE Dataset overload


function varargout = BEARapp()
% Launch the appropriate version of the BEAR app
if verLessThan('matlab','9.9')
    eval('app = BEARapp20a');
else
    eval('app = BEARapp21a');
end

if nargout == 1
    varargout = {app};
end


 function ob = abs(ob)
%function ob = abs(ob)

if isempty(ob.handle_abs)
	error 'no abs() method for this object'
end

ob = ob.handle_abs(ob);


function [tpl, str] = parse(tpl,target,handle,append)
%TEMPLATE/PARSE Fill in replacement fields with the class properties
%  [TPL, STR] = PARSE(TPL,TARGET,HANDLE) fills in the replacement field
%  HANDLE using previously defined variables of template TPL and store
%  it in field TARGET. HANDLE can also be a cell array of field names.
%  Output is also provided in output STR (content of TARGET).
%  [TPL, STR] = PARSE(TPL,TARGET,HANDLE,APPEND) allows to specify if
%  TARGET field is reseted before being filled or if new content is
%  appended to the previous one.

%  Copyright (C) 2003 Guillaume Flandin <Guillaume@artefact.tk>
%  $Revision: 1.0 $Date: 2003/05/05 22:19:51 $

narginchk(3,4);
if nargin == 3
	append = 0;
end

if iscellstr(handle)
	for i=1:length(handle)
		[tpl, str] = subst(tpl,handle{i});
		tpl = set(tpl,'var',target,str);
	end
elseif ischar(handle)
	[tpl, str] = subst(tpl,handle);
	if append
		tpl = set(tpl,'var',target,[get(tpl,'var',target) str]);
	else
		tpl = set(tpl,'var',target,str);
	end
else
	error('[Template] Badly formed handle.');
end


function colors = solver(A,targetIndex)
   colors = [];
end


function boolean  = isobj(handle,currobj)
    % ISOBJ	True for objects
    %       ISOBJ(X) returns 1's where the elements of X are
    % 		valid handles and 0's where they are not.
    %
    % Keith Rogers 11/30/93
    if ~isempty(handle) ; currobj = 0; boolean = 0;return; end
    if nargin == 1
        currobj = 0;
    end
    boolean = (handle == currobj);
    if(strcmp(get(currobj,'Type'),'axes'));
        boolean = boolean | (handle == get(currobj,'xlabel'));
        boolean = boolean | (handle == get(currobj,'ylabel'));
        boolean = boolean | (handle == get(currobj,'zlabel'));
        boolean = boolean | (handle == get(currobj,'title'));
    end
    children = get(currobj,'Children');
    if(children)
        for i = 1:length(children)
            boolean = boolean | isobj(handle,children(i));
        end
    end


%GRABFG Framegrabber grabbing image function.
%
%SYNTAX imgmatrix = grabfg(devinfo) or
%       moviematrix = grabfg(devinfo,imgmatrix) or
%       [imgmatrix, moviematrix] = grabfg(devinfo) or
%       grabfg(devinfo, imgmatrix) or
%       grabfg(devinfo,imgmatrix,moviematrix) or
%       grabfg(devinfo,imgmatrix,moviematrix, scaling factor)
%
%DESCRIPTION This function will let the user grab an
%image from a opened framegrabber device. The user has
%to give the framegrabbers device information structure
%as input argument. Executing the openfg function has
%retrieved this device information structure
%
%INPUT The device information structure (devinfo) of
%an opened framegrabber device and the eventually the
%preallocated image matrix  (imgmatrix);
%
%REMARKS It will be verified if the input is a valid
%device information structure. If it's valid, it will
%try to get the deviceid from the device information
%structure. If the second input is given, the function
%will check the size of the second input and checks if
%there's enough room to store the grabbed image. If
%there's enough room the variable (of type imgmatrix)
%will be overwritten without creating a new matrix,
%else a new matrix will be created and stored in that
%variable.
%
%OUTPUT If the input isn't valid or the framegrabber
%isn't able to grab an image, some error message will
%be shown to the user. If everything's ok it will return
%the grabbed image stored in a Matlab matrix (imgmatrix).
%
%The image is stored in transposed way. To show the image
%correctly in Matlab you can use the accent ' on the
%imgmatrix to correct it (=> image(imgmatrix') )
%
%Grabfg also generates true matlab movie matrices. The
%resolution reduction factor defaults to 2.

%EXAMPLE There are two ways to use this function. Both
%will be shown here. Variable m is the device info
%structure which was returned from the function openfg.
%
%1. Just grab an image and store it in the variable im,
%   type:
%               im = grabfg(m);
%
%   To show this image in Matlab
%   (in the right way, not transposed) type:
%               image(im');
%
%2. Just grab an image and movie matrix and store it in the
%   variable im and movie, type:
%               [im,movie] = grabfg(m);
%
%   To show the movie matrix in Matlab
%   (the movie matrix does not need to be transposed) type:
%               movie(movie);
%
%3. First create a matrix of the appropriate size and
%   type where the image will be stored:
%               im = uint8(zeros(768,576));
%
%   Then grab the image:
%               grabfg(m,im);
%
%   To show the image in Matlab type:
%               image(im');

disp('Error: grabfg not found.')


function [selected_file, selected_path] = rri_select_file(varargin)
%
%  USAGE: [selected_file, selected_path] = ...
%             rri_select_file(dir_name, fig_title)
%
%  Allow user to select a file from a list of Matlab competible
%	file format
%
%  Example:
%
%    [selected_file, selected_path] = ...
%		rri_select_file('/usr','Select Data File');
%
%  See Also RRI_GETFILES

%  -- Created June 2001 by Wilkin Chau, Rotman Research Institute
%
%  use rri_select_file to open & save Matlab recognized format
%  -- Modified Dec 2002 by Jimmy Shen, Rotman Research Institute
%

   if nargin == 0 | ischar(varargin{1}) 	% create rri_select_file figure

      dir_name = '';
      fig_title = 'Select a File';

      if nargin > 0
         dir_name = varargin{1};
      end

      if nargin > 1
         fig_title = varargin{2};
      end

      Init(fig_title,dir_name);
      uiwait;                           % wait for user finish

      selected_path = getappdata(gcf,'SelectedDirectory');
      selected_file = getappdata(gcf,'SelectedFile');

      cd (getappdata(gcf,'StartDirectory'));
      close(gcf);
      return;
   end;

   %  clear the message line,
   %
   h = findobj(gcf,'Tag','MessageLine');
   set(h,'String','');

   action = varargin{1}{1};

   %  change 'File format':
   %  update 'Files' & 'File selection' based on file pattern
   %
   if strcmp(action,'EditFilter'),
      EditFilter;

   %  run delete_fig when figure is closing
   %
   elseif strcmp(action,'delete_fig'),
      delete_fig;

   %  select 'Directories':
   %  go into the selected dir
   %  update 'Files' & 'File selection' based on file pattern
   %
   elseif strcmp(action,'select_dir'),
      select_dir;

   %  select 'Files':
   %  update 'File selection'
   %
   elseif strcmp(action,'select_file'),
      select_file;

   %  change 'File selection':
   %  if it is a file, select that,
   %  if it is more than a file (*), select those,
   %  if it is a directory, select based on file pattern
   %
   elseif strcmp(action,'EditSelection'),
      EditSelection;

   %  clicked 'Select'
   %
   elseif strcmp(action,'DONE_BUTTON_PRESSED'),
      h = findobj(gcf,'Tag','SelectionEdit');
      [filepath,filename,fileext] = fileparts(get(h,'String'));

      if isempty(filepath) | isempty(filename) | isempty(fileext)
         setappdata(gcf,'SelectedDirectory',[]);
         setappdata(gcf,'SelectedFile',[]);
      else
         if ~strcmp(filepath(end),filesep)		% not end with filesep
            filepath = [filepath filesep];		% add a filesep to filepath
         end

         setappdata(gcf,'SelectedDirectory',filepath);
         setappdata(gcf,'SelectedFile',[filename fileext]);
      end

      if getappdata(gcf,'ready')			% ready to exit
         uiresume;
      end

   %  clicked 'cancel'
   %
   elseif strcmp(action,'CANCEL_BUTTON_PRESSED'),
      setappdata(gcf,'SelectedDirectory',[]);
      setappdata(gcf,'SelectedFile',[]);
      set(findobj(gcf,'Tag','FileList'),'String','');
      uiresume;
   end;

   return;

% --------------------------------------------------------------------
function Init(fig_title,dir_name),

   StartDirectory = pwd;
   if isempty(StartDirectory),
       StartDirectory = filesep;
   end;

   filter_disp = {'JPEG image (*.jpg)', ...
	'TIFF image, compressed (*.tif)', ...
	'EPS Level 1 (*.eps)', ...
	'Adobe Illustrator 88 (*.ai)', ...
	'Enhanced metafile (*.emf)', ...
	'Matlab Figure (*.fig)', ...
	'Matlab M-file (*.m)', ...
	'Portable bitmap (*.pbm)', ...
	'Paintbrush 24-bit (*.pcx)', ...
	'Portable Graymap (*.pgm)', ...
	'Portable Network Graphics (*.png)', ...
	'Portable Pixmap (*.ppm)', ...
   };

   filter_string = {'*.jpg', ...
	'*.tif', ...
	'*.eps', ...
	'*.ai', ...
	'*.emf', ...
	'*.fig', ...
	'*.m', ...
	'*.pbm', ...
	'*.pcx', ...
	'*.pgm', ...
	'*.png', ...
	'*.ppm', ...
   };

%   filter_disp = char(filter_disp);
   filter_string = char(filter_string);

   margine = 0.05;
   line_height = 0.07;
   char_height = line_height*0.8;

   save_setting_status = 'on';
   rri_select_file_pos = [];

   try
      load('pls_profile');
   catch
   end

   if ~isempty(rri_select_file_pos) & strcmp(save_setting_status,'on')

      pos = rri_select_file_pos;

   else

      w = 0.4;
      h = 0.6;
      x = (1-w)/2;
      y = (1-h)/2;

      pos = [x y w h];

   end

   h0 = figure('parent',0, 'Color',[0.8 0.8 0.8], ...
        'Units','normal', ...
        'Name',fig_title, ...
        'NumberTitle','off', ...
        'MenuBar','none', ...
        'Position', pos, ...
        'deleteFcn','rri_select_file({''delete_fig''});', ...
        'WindowStyle', 'modal', ...
        'Tag','GetFilesFigure', ...
        'ToolBar','none');

   x = margine;
   y = 1 - 1*line_height - margine;
   w = 1-2*x;
   h = char_height;

   pos = [x y w h];

   h1 = uicontrol('Parent',h0, ...		% Filter Label
        'Style','text', ...
        'Units','normal', ...
        'BackgroundColor',[0.8 0.8 0.8], ...
	'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'Position', pos, ...
        'String','Choose one of the file format:', ...
        'Tag','FilterLabel');

   y = 1 - 2*line_height - margine + line_height*0.2;
   w = 1-2*x;

   pos = [x y w h];

   h_filter = uicontrol('Parent',h0, ...	% Filter list
        'Style','popupmenu', ...
        'Units','normal', ...
        'BackgroundColor',[1 1 1], ...
	'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'Position', pos, ...
        'String', filter_disp, ...
        'user', filter_string, ...
	'value', 1, ...
        'Callback','rri_select_file({''EditFilter''});', ...
        'Tag','FilterEdit');

   y = 1 - 3*line_height - margine;
   w = 0.5 - x - margine/2;

   pos = [x y w h];

   h1 = uicontrol('Parent',h0, ...            % Directory Label
        'Style','text', ...
        'Units','normal', ...
        'BackgroundColor',[0.8 0.8 0.8], ...
	'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'ListboxTop',0, ...
        'Position', pos, ...
        'String','Directories', ...
        'Tag','DirectoryLabel');

   x = 0.5;
   y = 1 - 3*line_height - margine;
   w = 0.5 - margine;

   pos = [x y w h];

   h1 = uicontrol('Parent',h0, ...            % File Label
        'Style','text', ...
        'Units','normal', ...
        'BackgroundColor',[0.8 0.8 0.8], ...
	'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'ListboxTop',0, ...
        'Position', pos, ...
        'String','Files', ...
        'Tag','FileLabel');

   x = margine;
   y = 4*line_height + margine;
   w = 0.5 - x - margine/2;
   h = 1 - 7*line_height - 2*margine;

   pos = [x y w h];

   h_dir = uicontrol('Parent',h0, ...            % Directory Listbox
        'Style','listbox', ...
        'Units','normal', ...
	'fontunit','normal', ...
        'FontSize',0.08, ...
        'HorizontalAlignment','left', ...
        'Interruptible', 'off', ...
        'ListboxTop',1, ...
        'Position', pos, ...
        'String', '', ...
        'Callback','rri_select_file({''select_dir''});', ...
        'Tag','DirectoryList');

   x = 0.5;
   y = 4*line_height + margine;
   w = 0.5 - margine;
   h = 1 - 7*line_height - 2*margine;

   pos = [x y w h];

   h_file = uicontrol('Parent',h0, ...            % File Listbox
        'Style','listbox', ...
        'Units','normal', ...
	'fontunit','normal', ...
        'FontSize',0.08, ...
        'HorizontalAlignment','left', ...
        'ListboxTop',1, ...
        'Position', pos, ...
        'String', '', ...
        'Callback','rri_select_file({''select_file''});', ...
        'Tag','FileList');

   x = margine;
   y = 3*line_height + margine - line_height*0.2;
   w = 1-2*x;
   h = char_height;

   pos = [x y w h];

   h1 = uicontrol('Parent',h0, ...            % Selection Label
        'Style','text', ...
        'Units','normal', ...
        'BackgroundColor',[0.8 0.8 0.8], ...
        'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'Position', pos, ...
        'String','File you selected:', ...
        'Tag','SelectionLabel');

   y = 2*line_height + margine;
   w = 1-2*x;

   pos = [x y w h];

   h_select = uicontrol('Parent',h0, ...            % Selection Edit
        'Style','edit', ...
        'Units','normal', ...
        'BackgroundColor',[1 1 1], ...
        'fontunit','normal', ...
        'FontSize',0.5, ...
        'HorizontalAlignment','left', ...
        'Position', pos, ...
        'String', '', ...
        'Callback','rri_select_file({''EditSelection''});', ...
        'Tag','SelectionEdit');

   x = 2*margine;
   y = line_height/2 + margine;
   w = 0.2;
   h = line_height;

   pos = [x y w h];

   h_done = uicontrol('Parent',h0, ...                      % DONE
        'Units','normal', ...
        'fontunit','normal', ...
        'FontSize',0.5, ...
        'ListboxTop',0, ...
        'Position', pos, ...
        'HorizontalAlignment','center', ...
        'String','Save', ...			% 'Select', ...
        'Callback','rri_select_file({''DONE_BUTTON_PRESSED''});', ...
        'Tag','DONEButton');

   x = 1 - x - w;

   pos = [x y w h];

   h_cancel = uicontrol('Parent',h0, ...                      % CANCEL
        'Units','normal', ...
        'fontunit','normal', ...
        'FontSize',0.5, ...
        'ListboxTop',0, ...
        'Position', pos, ...
        'HorizontalAlignment','center', ...
        'String','Cancel', ...
        'Callback','rri_select_file({''CANCEL_BUTTON_PRESSED''});', ...
        'Tag','CANCELButton');

   if isempty(dir_name)
      dir_name = StartDirectory;
   end

   set(h_select,'string',dir_name);

   filter_select = get(h_filter,'value');
   filter_pattern = filter_string(filter_select,:);

   setappdata(gcf,'FilterPattern',deblank(filter_pattern));
   setappdata(gcf,'filter_string',filter_string);

   setappdata(gcf,'h_filter', h_filter);
   setappdata(gcf,'h_dir', h_dir);
   setappdata(gcf,'h_file', h_file);
   setappdata(gcf,'h_select', h_select);
   setappdata(gcf,'h_done', h_done);
   setappdata(gcf,'h_cancel', h_cancel);
   setappdata(gcf,'StartDirectory',StartDirectory);

   EditSelection;

   h_file = getappdata(gcf,'h_file');
   if isempty(get(h_file,'string'))
      setappdata(gcf,'ready',0);
   else
      setappdata(gcf,'ready',1);
   end

   return;					% Init

%  called by all the actions, to update 'Directories' or 'Files'
%  based on filter_pattern. Select first file in filelist.
%
% --------------------------------------------------------------------

function update_dirlist;

   filter_path = getappdata(gcf,'curr_dir');
   filter_pattern = getappdata(gcf,'FilterPattern');

   if exist(filter_pattern) == 2	% user input specific filename
      is_single_file = 1;		% need manually take path out later
   else
      is_single_file = 0;
   end

   % take the file path out from filter_pattern
   %
   [fpath fname fext] = fileparts(filter_pattern);
   filter_pattern = [fname fext];

   dir_struct = dir(filter_path);
   if isempty(dir_struct)
      msg = 'ERROR: Directory not found!';
      uiwait(msgbox(msg,'File Selection Error','modal'));
      return;
   end;

   old_pointer = get(gcf,'Pointer');
   set(gcf,'Pointer','watch');

   dir_list = dir_struct(find([dir_struct.isdir] == 1));
   [sorted_dir_names,sorted_dir_index] = sortrows({dir_list.name}');

   dir_struct = dir([filter_path filesep filter_pattern]);
   if isempty(dir_struct)
      sorted_file_names = [];
   else
      file_list = dir_struct(find([dir_struct.isdir] == 0));

      if is_single_file			% take out path
         tmp = file_list.name;
         [fpath fname fext] = fileparts(tmp);
         file_list.name = [fname fext];
      end

      [sorted_file_names,sorted_file_index] = sortrows({file_list.name}');
   end;

   disp_dir_names = [];			% if need full path, use this
					% instead of sorted_dir_names
   for i=1:length(sorted_dir_names)
      tmp = [filter_path filesep sorted_dir_names{i}];
      disp_dir_names = [disp_dir_names {tmp}];
   end

   h = findobj(gcf,'Tag','DirectoryList');
   set(h,'String',sorted_dir_names,'Value',1);

   h = findobj(gcf,'Tag','FileList');
   set(h,'String',sorted_file_names,'value',1);

   h_select = getappdata(gcf,'h_select');
   if strcmp(filter_path(end),filesep)		% filepath end with filesep
      filter_path = filter_path(1:end-1);	% take filesep out
   end

   if isempty(sorted_file_names)
      set(h_select,'string',[filter_path filesep]);
   else
      set(h_select,'string',[filter_path filesep sorted_file_names{1}]);
   end

   set(gcf,'Pointer',old_pointer);

   return; 					% update_dirlist

%  change 'File format':
%  update 'Files' & 'File selection' based on file pattern
%
% --------------------------------------------------------------------

function EditFilter()

   filter_select = get(gcbo,'value');
   filter_string = getappdata(gcf,'filter_string');
   filter_pattern = filter_string(filter_select,:);
   filter_path = getappdata(gcf,'curr_dir');

   % update filter_pattern
   setappdata(gcf,'FilterPattern',deblank(filter_pattern));

   if isempty(filter_path),
       filter_path = filesep;
   end;

   update_dirlist;

   h_file = getappdata(gcf,'h_file');
   if isempty(get(h_file,'string'))
      setappdata(gcf,'ready',0);
   else
      setappdata(gcf,'ready',1);
   end

   return;					% EditFilter

%  select 'Directories':
%  go into the selected dir
%  update 'Files' & 'File selection' based on file pattern
%
% --------------------------------------------------------------------

function select_dir()

   listed_dir = get(gcbo,'String');
   selected_dir_idx = get(gcbo,'Value');
   selected_dir = listed_dir{selected_dir_idx};
   curr_dir = getappdata(gcf,'curr_dir');

   %  update the selection box
   %
   try
      cd ([curr_dir filesep selected_dir]);
   catch
      msg = 'ERROR: Cannot access directory';
      uiwait(msgbox(msg,'File Selection Error','modal'));
      return;
   end;

   if isempty(pwd)
      curr_dir = filesep;
   else
      curr_dir = pwd;
   end;

   setappdata(gcf,'curr_dir',curr_dir);
   update_dirlist;

   h_file = getappdata(gcf,'h_file');
   if isempty(get(h_file,'string'))
      setappdata(gcf,'ready',0);
   else
      setappdata(gcf,'ready',1);
   end

   return;					% select_dir

%  select 'Files':
%  update 'File selection'
%
% --------------------------------------------------------------------

function select_file()

   setappdata(gcf,'ready',1);
   listed_file = get(gcbo,'String');
   selected_file_idx = get(gcbo,'Value');
   selected_file = listed_file{selected_file_idx};
   curr_dir = getappdata(gcf,'curr_dir');

   if strcmp(curr_dir(end),filesep)		% filepath end with filesep
      curr_dir = curr_dir(1:end-1);	% take filesep out
   end

   h_select = getappdata(gcf,'h_select');
   set(h_select,'string',[curr_dir filesep selected_file]);

   return;					% select_file

%  change 'File selection':
%  if it is a file, select that,
%  if it is more than a file (*), select those,
%  if it is a directory, select based on file pattern
%
% --------------------------------------------------------------------

function EditSelection()

   filter_string = getappdata(gcf,'filter_string');
   h_select = getappdata(gcf,'h_select');
   selected_file = get(h_select,'string');

   if exist(selected_file) == 7			% if user enter a dir
      setappdata(gcf,'ready',0);
      setappdata(gcf,'curr_dir',selected_file);		% get new dir
      update_dirlist;
   else

      setappdata(gcf,'ready',1);

      [fpath fname fext]= fileparts(selected_file);
      if exist(fpath) ~=7			% fpath is not a dir
         setappdata(gcf,'ready',0);
         msg = 'ERROR: Cannot access directory';
         uiwait(msgbox(msg,'File Selection Error','modal'));
      end

      %  if the file format user entered is not supported by matlab
      if isempty(strmatch(['*',fext],filter_string,'exact'))
         setappdata(gcf,'ready',0);
         msg = 'ERROR: File format is not supported by Matlab.';
         uiwait(msgbox(msg,'File Selection Error','modal'));
      end

   end

   return;					% EditSelection

% --------------------------------------------------------------------

function delete_fig()

   try
      load('pls_profile');
      pls_profile = which('pls_profile.mat');

      rri_select_file_pos = get(gcbf,'position');

      save(pls_profile, '-append', 'rri_select_file_pos');
   catch
   end

   return;


%   This directory contains the test suite for the mUnit test framework.  Before
%   running the test suite, do the following:
%
%   1. Make sure the mUnit test framework directory is on your path
%   2. Make sure the helper_classes subdirectory of the test directory is on
%      your path.
%   3. Make the test directory your current directory.
%
%   To run the test suite:
%
%       run(TestSuite())

%   Steven L. Eddins
%   Copyright 2008 The MathWorks

help Readme


function runGmsh(geo_name,gmsh_path)
    if isempty(gmsh_path)
        gmsh_path='C:\Users\icberg\OneDrive - UAB - The University of Alabama at Birmingham\Documents\FEM Model Heart Chip';
    end

    oldFolder = cd(gmsh_path);
    system(['gmsh.exe ' geo_name ' -0']);
    cd(oldFolder)
end
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function isRemoved = db_delete_protocol(isUserConfirm, isRemoveFiles)
% DB_DELETE_PROTOCOL: Remove current protocol from database.
%
% USAGE:  db_delete_protocol(isUserConfirm, isRemoveFiles)  : Remove protocol #iProtocol from protocols list
%
% INPUT:
%     - isUserConfirm : If 0, do not ask user confirmation (default=1)
%     - isRemoveFiles : If 1, delete all the files in this protocol from the hard drive
%                       If 0, keep the files on the hard drive
%                       If empty or not specified: ask user

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2008-2017

global GlobalData;

%% ===== PARSE INPUTS =====
% Options
if (nargin < 2)
    error('Invalid call to db_delete_protocol().');
end
% Get Protocols list structures (Infos, Subjects, Studies)
sProtocolsListInfo     = GlobalData.DataBase.ProtocolInfo;
sProtocolsListSubjects = GlobalData.DataBase.ProtocolSubjects;
sProtocolsListStudies  = GlobalData.DataBase.ProtocolStudies;
iProtocol              = GlobalData.DataBase.iProtocol;
if isempty(sProtocolsListInfo) || isempty(iProtocol)
    disp('BST> No protocol selected, nothing to delete.');
    return;
end

%% ===== ASK USER CONFIRMATION =====
if isUserConfirm
    % Warning string
    if ~isRemoveFiles
        strWarn = '(Subjects and datasets directories will not be deleted)';
    else
        strWarn = ['
WARNING: All the files will be permanently deleted from your hard drive.
' ...
                   bst_fileparts(sProtocolsListInfo(iProtocol).STUDIES) ''];
    end
    % Display dialog box
    isConfirmed = java_dialog('confirm', ['Remove protocol ''' sProtocolsListInfo(iProtocol).Comment ''' from Brainstorm database ?
' ...
                                          strWarn '

'], sprintf('Remove protocol #%d', iProtocol));
    if ~isConfirmed
        isRemoved = 0;
        return
    end
end

%% ===== REMOVE FILES =====
if isRemoveFiles
    bst_progress('start', 'Delete Protocol', 'Removing all files...');
    % Remove all the contents of STUDIES and SUBJECTS folders
    file_delete( {sProtocolsListInfo(iProtocol).STUDIES, sProtocolsListInfo(iProtocol).SUBJECTS}, 1, 2);
    % If the parent folder (protocol folder) is empty: remove it
    ProtocolDir = bst_fileparts(sProtocolsListInfo(iProtocol).STUDIES);
    listFiles = dir(fullfile(ProtocolDir, '*'));
    listFiles = setdiff({listFiles.name}, {'..','.'});
    if isempty(listFiles)
        rmdir(ProtocolDir);
    else
        warning(['Protocol folder is not empty, cannot be deleted:' ProtocolDir]);
    end
end

%% ===== REMOVE PROTOCOL =====
bst_progress('start', 'Delete Protocol', 'Removing protocol...');
sProtocolsListInfo(iProtocol)     = [];
sProtocolsListSubjects(iProtocol) = [];
sProtocolsListStudies(iProtocol)  = [];
% Update database
GlobalData.DataBase.ProtocolInfo      = sProtocolsListInfo;
GlobalData.DataBase.ProtocolSubjects  = sProtocolsListSubjects;
GlobalData.DataBase.ProtocolStudies   = sProtocolsListStudies;
GlobalData.DataBase.isProtocolLoaded(iProtocol)   = [];
GlobalData.DataBase.isProtocolModified(iProtocol) = [];
% Update protocols ComboBox
gui_brainstorm('UpdateProtocolsList');

% Get new protocol to select
if isempty(sProtocolsListInfo)
    iProtocol = 0;
elseif (iProtocol == 1)
    iProtocol = 1;
else
    iProtocol = iProtocol - 1;
end
% Select current protocol in combo list
gui_brainstorm('SetCurrentProtocol', iProtocol);

% Save database
% db_save();
isRemoved = 1;

bst_progress('stop');


function [] = printCellList(fid, cellList, indent)
% Output the values in cellList one per line with indent in front
for k = 1:length(cellList)
    fprintf(fid, '%s%s\n',indent, num2str(cellList{k}));
end


function retval = isOctave()
    %ISOCTAVE  Return true if the environment is Octave
    %
    %     retval = mexopencv.isOctave()
    %
    % ## Output
    % * __retval__ true if running in Octave, false otherwise (MATLAB).
    %
    % See also: ver, version
    %

    persistent cacheval;
    if isempty(cacheval)
        cacheval = (exist('OCTAVE_VERSION', 'builtin') > 0);
    end
    retval = cacheval;
end