classdef RoaPlot < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        RootFigure             matlab.ui.Figure
        PlotActivityin3DPanel  matlab.ui.container.Panel
        WindowSpinnerLabel     matlab.ui.control.Label
        WindowSpinner          matlab.ui.control.Spinner
        PlotButton             matlab.ui.control.Button
        UIAxes                 matlab.ui.control.UIAxes
    end

    
    properties (Access = private)
        manager
        application
        data_change_listener
        view_data_change_listener
        view
        
        frame_indicator
        window_indicators
    end
    
    methods (Access = private)
        
        function handle_view_changed(app,sr,ev)
            if ~isvalid(app)
                return;
            end
            
            % clear old event listeners:
            if ~isempty(app.data_change_listener)
                delete(app.data_change_listener);
            end
            if ~isempty(app.view_data_change_listener)
                delete(app.view_data_change_listener);
            end
    
            app.manager = ev.viewmanager;

            % the new items could be empty - in whcih case we simply hide
            % outselves:
            if isempty(ev.viewmanager) || isempty(ev.to) || ~ev.viewmanager.data.has("roa_param")
                app.view = [];
                app.RootFigure.Visible = false;
                return
            else
                app.RootFigure.Visible = true;
                % this steals focus, so we must focus top view again:
                ev.viewmanager.focus_top_view();
                
                app.data_change_listener = addlistener(app.manager.data, ...
                   'on_data_changed', @(~, ~) app.handle_data_changed());
                
                app.view = ev.to;
                
                app.manager.data.assign_changeflag("roa_traces","roa_plot_init");
                app.view.data.assign_changeflag("channel","roa_plot_init");
            
                app.manager.data.assign_changeflag("current_frame","roa_plot_update");
                app.manager.data.assign_changeflag("roa_plot_window","roa_plot_update");
                
                app.view_data_change_listener = addlistener(ev.to.data, ...
                   'on_data_changed', @(~, ~) app.handle_data_changed());
                
                app.handle_data_changed();
            end
        end
        
        function init_plot(app)
            [~, ~, v_read] = app.view.data.shorts();
            [~, ~, m_read,m_flagged] = app.manager.data.shorts();
            
            chan = v_read("channel");
            roa_traces = m_read("roa_traces");
            current_frame = m_read("current_frame");
            
            cla(app.UIAxes);
            
            if isempty(roa_traces)
                return;
            end
            
            trace = roa_traces.roa_density_trace{chan};
            
            plot(app.UIAxes,trace);
            hold(app.UIAxes,"on");
            
            ymax = max(trace);
            if ymax == 0
                y_limits = [0,1];
            else
                y_limits = [0,max(trace)];
            end
            
            window = app.WindowSpinner.Value;
            window = m_read("roa_plot_window",window);
            
            left_window_indicator = plot(app.UIAxes, ...
                [current_frame-window,current_frame-window], ...
                y_limits, ...
                "Color","green");
            
            right_window_indicator = plot(app.UIAxes, ...
                [current_frame+window,current_frame+window], ...
                y_limits, ...
                "Color","green");
            
            frame_indicator = plot(app.UIAxes,[current_frame,current_frame],y_limits,'Color','red');
            
            app.window_indicators = [left_window_indicator,right_window_indicator];
            app.frame_indicator = frame_indicator;
        end
        
        function handle_data_changed(app)
            
            if ~isvalid(app) || isempty(app.manager) || ~isvalid(app.manager) || isempty(app.view)
                return;
            end
            
            [~, ~, m_read,m_flagged] = app.manager.data.shorts();
            [v_has, ~, v_read,v_flagged] = app.view.data.shorts();
            
            roa_overlay_mode = v_read("roa_overlay_mode");
            
            % Only display the plotting tool if the overlay mode is showing
            % the processed data.
            if roa_overlay_mode == "final_results"
                app.RootFigure.Visible = true;
            else
                app.RootFigure.Visible = false;
                return;
            end
            
            if m_flagged("roa_plot_init")  || v_flagged("roa_plot_init")
                app.init_plot();
            end
            
            if m_flagged("roa_plot_update") && ~isempty(app.frame_indicator)
                current_frame = m_read("current_frame");
                app.frame_indicator.XData = [current_frame,current_frame];
                
                window = app.WindowSpinner.Value;
                window = m_read("roa_plot_window",window);
                app.window_indicators(1).XData = [current_frame-window,current_frame-window];
                app.window_indicators(2).XData = [current_frame+window,current_frame+window];
            end
        end
        
        
        function plot_3d(app)
            ch = app.view.data.read("channel");
            roa_mask = app.manager.data.read("roa_mask_ch"+ch);
            
            last_frame = app.manager.data.read("frames");
            last_frame = last_frame(end);
            current_frame = app.manager.data.read("current_frame");
            window = app.manager.data.read("roa_plot_window");
            
            ts = app.manager.data.read("tseries");
            
            st = current_frame - window;
            en = current_frame + window;
            st = max(st,1);
            en = min(en,last_frame);
            
            max_samples = min(en-st+1,500);
            
            I = round(linspace(st,en,max_samples));
            
            begonia.logging.log(1,"Loading mask");
            roa_mask = roa_mask(:,:,:);
            roa_mask = roa_mask(:,:,I);
            begonia.logging.log(1,"Making 3D plot");
            begonia.processing.roa.plot_roa_3d(roa_mask,ts.dx,ts.dy,ts.dt); 
            begonia.logging.log(1,"Plot finished");
        end
    end
    

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

        % Code that executes after component creation
        function startupFcn(app, application)
            addlistener(application, 'on_view_changed',...
                @app.handle_view_changed);
            
            app.application = application;
        end

        % Value changing function: WindowSpinner
        function WindowSpinnerValueChanging(app, event)
            if ~isempty(app.manager)
                app.manager.data.write("roa_plot_window",event.Value);
            end
        end

        % Button pushed function: PlotButton
        function PlotButtonPushed(app, event)
            
            
            editor = app.manager.data.read("editor");
            
            if isempty(editor)
                disp("Rendering ROA in 3D usually takes a very long time.");
                app.plot_3d();
            else
                disp("Rendering ROA in 3D usually takes a very long time. Run the action queue to start the processing.")
                action = xylobium.dledit.Action("Plot ROA in 3D", @(~,~,~)app.plot_3d());
                action.accept_multiple_dlocs = false;
                action.can_execute_without_dloc = true;
                editor.handleQueueRequested(action);
                editor.jobview.show(true);
            end
        end
    end

    % Component initialization
    methods (Access = private)

        % Create UIFigure and components
        function createComponents(app)

            % Create RootFigure and hide until all components are created
            app.RootFigure = uifigure('Visible', 'off');
            app.RootFigure.Position = [100 100 702 358];
            app.RootFigure.Name = 'ROA Plotting Tool';

            % Create PlotActivityin3DPanel
            app.PlotActivityin3DPanel = uipanel(app.RootFigure);
            app.PlotActivityin3DPanel.Title = 'Plot Activity in 3D';
            app.PlotActivityin3DPanel.Position = [7 7 692 69];

            % Create WindowSpinnerLabel
            app.WindowSpinnerLabel = uilabel(app.PlotActivityin3DPanel);
            app.WindowSpinnerLabel.HorizontalAlignment = 'right';
            app.WindowSpinnerLabel.Position = [16 14 49 22];
            app.WindowSpinnerLabel.Text = 'Window';

            % Create WindowSpinner
            app.WindowSpinner = uispinner(app.PlotActivityin3DPanel);
            app.WindowSpinner.ValueChangingFcn = createCallbackFcn(app, @WindowSpinnerValueChanging, true);
            app.WindowSpinner.Limits = [1 Inf];
            app.WindowSpinner.Tooltip = {'Number of frames on each side of the current frame to include in the 3D plot.'};
            app.WindowSpinner.Position = [80 14 100 22];
            app.WindowSpinner.Value = 1;

            % Create PlotButton
            app.PlotButton = uibutton(app.PlotActivityin3DPanel, 'push');
            app.PlotButton.ButtonPushedFcn = createCallbackFcn(app, @PlotButtonPushed, true);
            app.PlotButton.Tooltip = {'Plotting in 3D takes a long time. Consider using a windows of 50 frames or less first. A volume with more than 500 frames will be downsampled to 500 frames. '};
            app.PlotButton.Position = [253 14 100 22];
            app.PlotButton.Text = 'Plot';

            % Create UIAxes
            app.UIAxes = uiaxes(app.RootFigure);
            title(app.UIAxes, 'Fraction of FOV Active')
            xlabel(app.UIAxes, 'Frame')
            ylabel(app.UIAxes, 'Fraction')
            zlabel(app.UIAxes, 'Z')
            app.UIAxes.PlotBoxAspectRatio = [2.97706422018349 1 1];
            app.UIAxes.Position = [1 91 698 268];

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

    % App creation and deletion
    methods (Access = public)

        % Construct app
        function app = RoaPlot(varargin)

            % Create UIFigure and components
            createComponents(app)

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

            % Execute the startup function
            runStartupFcn(app, @(app)startupFcn(app, varargin{:}))

            if nargout == 0
                clear app
            end
        end

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

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