classdef Channel < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        RootFigure             matlab.ui.Figure
        ChannelPanel           matlab.ui.container.Panel
        projection_select      matlab.ui.container.ButtonGroup
        raw_select             matlab.ui.control.RadioButton
        mean_select            matlab.ui.control.RadioButton
        std_select             matlab.ui.control.RadioButton
        max_select             matlab.ui.control.RadioButton
        mean_full_select       matlab.ui.control.RadioButton
        std_full_select        matlab.ui.control.RadioButton
        max_full_select        matlab.ui.control.RadioButton
        SampleframesLabel      matlab.ui.control.Label
        sample_input           matlab.ui.control.NumericEditField
        ScalebarCheckBox       matlab.ui.control.CheckBox
        ColormapDropDownLabel  matlab.ui.control.Label
        colormap_select        matlab.ui.control.DropDown
        ChannelLabel           matlab.ui.control.Label
        channel_select         matlab.ui.control.DropDown
        SmoothpixelsLabel      matlab.ui.control.Label
        spatial_smooth_input   matlab.ui.control.NumericEditField
        generate_refs_button   matlab.ui.control.Button
        RangePanel             matlab.ui.container.Panel
        LowSliderLabel         matlab.ui.control.Label
        low_slider             matlab.ui.control.Slider
        HighSliderLabel        matlab.ui.control.Label
        high_slider            matlab.ui.control.Slider
    end

    
    properties (Access = private)
        manager % Description
        view_change_listener % Description
        application % Description
        view_data_change_listener
    end
    
    
    methods (Access = public)
        
        % saves the tool's settings into the current view (if any)
        function write_to_view(app)
            view = app.manager.current_view;
            if ~view.has_module("channel")
                return;
            end
            
            selection = app.projection_select.SelectedObject.Text;
            
            scale = app.ScalebarCheckBox.Value;

            samples = app.sample_input.Value;
            
            xy_smooth = app.spatial_smooth_input.Value;
                       
            cmap = app.colormap_select.Value;
            if cmap == "Turbo"
                cmap = "begonia.colormaps.turbo";
            end
            
            low = app.low_slider.Value;
            high = app.high_slider.Value;
            
            view.data.write("channel_mode", selection, false);
            view.data.write("channel_samples", samples, false);
            view.data.write("channel_xy_smoothing", xy_smooth, false);
            view.data.write("channel_colormap", cmap, false);
            view.data.write("channel_low", low, false);
            view.data.write("channel_high", high, true);
            view.data.write("channel_scale", scale, false);
          
            %disp(string(datetime()) + " WROTE!")
        end
        
        % loads the settings from the current view into controls
        function load_from_view(app)
            view = app.manager.current_view;
            if isempty(view) || ~view.has_module("channel")
                return;
            end
            
            if view.data.check("channel_tool")
                selection = view.data.read("channel_mode");
                sidx = find(string({app.projection_select.Buttons.Text}) == selection);
                sobj = app.projection_select.Buttons(sidx);
                app.projection_select.SelectedObject = sobj;
                
                scale = view.data.read("channel_scale");
                app.ScalebarCheckBox.Value = scale;
                
                samples = view.data.read("channel_samples");
                app.sample_input.Value = samples;
                
                xy_smoothing = view.data.read("channel_xy_smoothing");
                app.spatial_smooth_input.Value = xy_smoothing;
                 
                cmap = view.data.read("channel_colormap");
                if cmap == "begonia.colormaps.turbo"
                    cmap = "Turbo";
                end
                app.colormap_select.Value = cmap;
             
                low = view.data.read("channel_low");
                app.low_slider.Value = low;
                
                high = view.data.read("channel_high");
                app.high_slider.Value = high;
            end
        end
        
    end
    
    methods (Access = private)
        
        function handle_view_changed(app, src, ev)
            delete(app.view_data_change_listener)
            
            % set current manage to be the new one provided by the event:
            app.manager = ev.viewmanager;
            view = ev.to;
            if isempty(app.manager) || isempty(view) || ~view.has_module("channel")
                return;
            end
            
            % populate the channel list:
            app.channel_select.Items = app.manager.data.read("channel_names");
            app.channel_select.ItemsData = app.manager.data.read("channels");
            
            v_flag_on = ["channel_samples", "channel_xy_smoothing", "channel", ...
                "channel_colormap", "channel_mode", "channel_low", "channel_high",...
                "channel_scale"];
            
            view.data.assign_changeflag(v_flag_on,"channel_tool");
            
            app.view_data_change_listener = addlistener(ev.to.data, ...
               'on_data_changed', @(~, ~) app.load_from_view());
            
            refs_made = ~isempty(app.manager.data.read("ref_img_avg_ch_1"));
            app.mean_full_select.Enable = refs_made;
            app.std_full_select.Enable = refs_made;
            app.max_full_select.Enable = refs_made;
        end
    end
    

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

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

        % Selection changed function: projection_select
        function handle_projection_select(app, event)
            app.write_to_view();
        end

        % Value changed function: sample_input
        function handle_sample_set(app, event)
            app.write_to_view();
        end

        % Value changing function: low_slider
        function handle_range_low_set(app, event)
            val = app.low_slider.Value;
            if val > 0.99
                val = 0.99;
            end
            if val > app.high_slider.Value
                app.high_slider.Value = val + 0.01;
            end
            app.write_to_view();
        end

        % Value changing function: high_slider
        function handle_range_high_set(app, event)
            val = app.high_slider.Value;
            if val < 0.01
                val = 0.01;
            end
            if val < app.low_slider.Value
                app.low_slider.Value = val - 0.005;
            end
            app.write_to_view();
        end

        % Value changed function: colormap_select
        function handle_colormap_changed(app, event)
            app.write_to_view();
        end

        % Close request function: RootFigure
        function handle_close(app, event)
            app.RootFigure.Visible = false;
        end

        % Key release function: RootFigure
        function handle_keyup(app, event)
            key = event.Key;
            app.manager.handle_keyboard('up', event)
        end

        % Value changed function: channel_select
        function handle_channel_changed(app, event)
            value = app.channel_select.Value;
            app.manager.current_view.data.write("channel", value);
        end

        % Value changed function: spatial_smooth_input
        function spatial_smooth_inputValueChanged(app, event)
            app.write_to_view();
        end

        % Button pushed function: generate_refs_button
        function handle_generate_refs(app, event)
            roiman.support.generate_tseries_reference_imgs(app.manager);
            app.mean_full_select.Enable = true;
            app.std_full_select.Enable = true;
            app.max_full_select.Enable = true;
        end

        % Value changed function: ScalebarCheckBox
        function handle_scale(app, event)
            value = app.ScalebarCheckBox.Value;
            app.write_to_view();
        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.AutoResizeChildren = 'off';
            app.RootFigure.Position = [100 100 248 407];
            app.RootFigure.Name = 'Channel settings';
            app.RootFigure.Resize = 'off';
            app.RootFigure.CloseRequestFcn = createCallbackFcn(app, @handle_close, true);
            app.RootFigure.KeyReleaseFcn = createCallbackFcn(app, @handle_keyup, true);

            % Create ChannelPanel
            app.ChannelPanel = uipanel(app.RootFigure);
            app.ChannelPanel.AutoResizeChildren = 'off';
            app.ChannelPanel.Title = 'Channel';
            app.ChannelPanel.Position = [1 83 247 325];

            % Create projection_select
            app.projection_select = uibuttongroup(app.ChannelPanel);
            app.projection_select.AutoResizeChildren = 'off';
            app.projection_select.SelectionChangedFcn = createCallbackFcn(app, @handle_projection_select, true);
            app.projection_select.Title = 'Projection';
            app.projection_select.Position = [9 106 226 158];

            % Create raw_select
            app.raw_select = uiradiobutton(app.projection_select);
            app.raw_select.Text = 'Raw';
            app.raw_select.Position = [11 112 58 22];
            app.raw_select.Value = true;

            % Create mean_select
            app.mean_select = uiradiobutton(app.projection_select);
            app.mean_select.Text = 'Mean';
            app.mean_select.Position = [11 87 65 22];

            % Create std_select
            app.std_select = uiradiobutton(app.projection_select);
            app.std_select.Text = 'Std.';
            app.std_select.Position = [11 63 65 22];

            % Create max_select
            app.max_select = uiradiobutton(app.projection_select);
            app.max_select.Text = 'Max';
            app.max_select.Position = [11 38 65 22];

            % Create mean_full_select
            app.mean_full_select = uiradiobutton(app.projection_select);
            app.mean_full_select.Text = 'Mean (full series)';
            app.mean_full_select.Position = [86 112 114 22];

            % Create std_full_select
            app.std_full_select = uiradiobutton(app.projection_select);
            app.std_full_select.Text = 'Std. (full series)';
            app.std_full_select.Position = [86 89 105 22];

            % Create max_full_select
            app.max_full_select = uiradiobutton(app.projection_select);
            app.max_full_select.Text = 'Max (full series)';
            app.max_full_select.Position = [86 64 107 22];

            % Create SampleframesLabel
            app.SampleframesLabel = uilabel(app.projection_select);
            app.SampleframesLabel.Position = [11 10 97 22];
            app.SampleframesLabel.Text = 'Sample (frames)';

            % Create sample_input
            app.sample_input = uieditfield(app.projection_select, 'numeric');
            app.sample_input.Limits = [1 100];
            app.sample_input.RoundFractionalValues = 'on';
            app.sample_input.ValueChangedFcn = createCallbackFcn(app, @handle_sample_set, true);
            app.sample_input.Position = [112 10 100 22];
            app.sample_input.Value = 30;

            % Create ScalebarCheckBox
            app.ScalebarCheckBox = uicheckbox(app.projection_select);
            app.ScalebarCheckBox.ValueChangedFcn = createCallbackFcn(app, @handle_scale, true);
            app.ScalebarCheckBox.Text = 'Scale bar';
            app.ScalebarCheckBox.Position = [87 38 73 22];

            % Create ColormapDropDownLabel
            app.ColormapDropDownLabel = uilabel(app.ChannelPanel);
            app.ColormapDropDownLabel.Position = [17 12 58 22];
            app.ColormapDropDownLabel.Text = 'Colormap';

            % Create colormap_select
            app.colormap_select = uidropdown(app.ChannelPanel);
            app.colormap_select.Items = {'Turbo', 'Bone', 'Copper', 'Hot', 'Cool', 'Summer', 'Autumn'};
            app.colormap_select.ValueChangedFcn = createCallbackFcn(app, @handle_colormap_changed, true);
            app.colormap_select.Position = [119 12 111 22];
            app.colormap_select.Value = 'Turbo';

            % Create ChannelLabel
            app.ChannelLabel = uilabel(app.ChannelPanel);
            app.ChannelLabel.Position = [12 274 53 22];
            app.ChannelLabel.Text = {'Channel:'; ''};

            % Create channel_select
            app.channel_select = uidropdown(app.ChannelPanel);
            app.channel_select.Items = {'CH1', 'CH2', 'CH3'};
            app.channel_select.ValueChangedFcn = createCallbackFcn(app, @handle_channel_changed, true);
            app.channel_select.Position = [85 274 150 22];
            app.channel_select.Value = 'CH1';

            % Create SmoothpixelsLabel
            app.SmoothpixelsLabel = uilabel(app.ChannelPanel);
            app.SmoothpixelsLabel.Position = [15 42 94 22];
            app.SmoothpixelsLabel.Text = 'Smooth (pixels)';

            % Create spatial_smooth_input
            app.spatial_smooth_input = uieditfield(app.ChannelPanel, 'numeric');
            app.spatial_smooth_input.Limits = [0 Inf];
            app.spatial_smooth_input.ValueChangedFcn = createCallbackFcn(app, @spatial_smooth_inputValueChanged, true);
            app.spatial_smooth_input.Position = [117 42 110 22];

            % Create generate_refs_button
            app.generate_refs_button = uibutton(app.ChannelPanel, 'push');
            app.generate_refs_button.ButtonPushedFcn = createCallbackFcn(app, @handle_generate_refs, true);
            app.generate_refs_button.WordWrap = 'on';
            app.generate_refs_button.FontSize = 10;
            app.generate_refs_button.Position = [9 74 226 22];
            app.generate_refs_button.Text = 'Generate full series reference images';

            % Create RangePanel
            app.RangePanel = uipanel(app.RootFigure);
            app.RangePanel.AutoResizeChildren = 'off';
            app.RangePanel.Title = 'Range';
            app.RangePanel.Position = [1 1 247 82];

            % Create LowSliderLabel
            app.LowSliderLabel = uilabel(app.RangePanel);
            app.LowSliderLabel.HorizontalAlignment = 'right';
            app.LowSliderLabel.FontSize = 10;
            app.LowSliderLabel.Position = [9 36 25 22];
            app.LowSliderLabel.Text = 'Low';

            % Create low_slider
            app.low_slider = uislider(app.RangePanel);
            app.low_slider.Limits = [0 1];
            app.low_slider.MajorTicks = [];
            app.low_slider.ValueChangingFcn = createCallbackFcn(app, @handle_range_low_set, true);
            app.low_slider.FontSize = 10;
            app.low_slider.Position = [55 45 166 3];

            % Create HighSliderLabel
            app.HighSliderLabel = uilabel(app.RangePanel);
            app.HighSliderLabel.HorizontalAlignment = 'right';
            app.HighSliderLabel.FontSize = 10;
            app.HighSliderLabel.Position = [9 13 26 22];
            app.HighSliderLabel.Text = 'High';

            % Create high_slider
            app.high_slider = uislider(app.RangePanel);
            app.high_slider.Limits = [0 1];
            app.high_slider.MajorTicks = [];
            app.high_slider.ValueChangingFcn = createCallbackFcn(app, @handle_range_high_set, true);
            app.high_slider.FontSize = 10;
            app.high_slider.Position = [56 22 165 3];
            app.high_slider.Value = 1;

            % 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 = Channel(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