classdef Mp4Maker < matlab.apps.AppBase

    % Properties that correspond to app components
    properties (Access = public)
        UIFigure                      matlab.ui.Figure
        OUTPUTFILEButton              matlab.ui.control.Button
        OUTPUTFILEEditField           matlab.ui.control.EditField
        PLAYBACKSPEEDEditField        matlab.ui.control.NumericEditField
        PLAYBACKSPEEDEditFieldLabel   matlab.ui.control.Label
        STARTFRAMEEditField           matlab.ui.control.EditField
        STARTFRAMEEditFieldLabel      matlab.ui.control.Label
        STARTTIMEEditField            matlab.ui.control.EditField
        STARTTIMEEditFieldLabel       matlab.ui.control.Label
        COLORDropDown                 matlab.ui.control.DropDown
        COLORDropDownLabel            matlab.ui.control.Label
        CONTRASTHIGHERSlider          matlab.ui.control.Slider
        CONTRASTHIGHERSliderLabel     matlab.ui.control.Label
        CONTRASTLOWERSlider           matlab.ui.control.Slider
        CONTRASTLOWERSliderLabel      matlab.ui.control.Label
        CHANNELDropDown               matlab.ui.control.DropDown
        CHANNELDropDownLabel          matlab.ui.control.Label
        CREATEMP4Button               matlab.ui.control.Button
        MP4FPSEditField               matlab.ui.control.NumericEditField
        DURATONSECONDSEditField       matlab.ui.control.NumericEditField
        DURATONSECONDSEditFieldLabel  matlab.ui.control.Label
        MP4FPSEditFieldLabel          matlab.ui.control.Label
        Slider                        matlab.ui.control.Slider
        Button_4                      matlab.ui.control.Button
        Button_3                      matlab.ui.control.Button
        Button_2                      matlab.ui.control.Button
        Button                        matlab.ui.control.Button
        UIAxes                        matlab.ui.control.UIAxes
    end

    
    properties (Access = private)
        ts

        mat1
        
        frame
        channel

        contrast_high
        contrast_low

    end
    
    methods (Access = private)
        
        function load_channel(app)
            % Load data.
            channel = ismember(app.CHANNELDropDown.Items,app.CHANNELDropDown.Value);
            channel = find(channel);
            app.channel = channel;
            app.mat1 = app.ts.get_mat(channel);

            % Change contrast limits.
            img = app.mat1(:,:,1);
            maxval = double(max(max(img)));
            minval = double(min(min(img)));
            valdiff = maxval - minval;
            limits = [minval - valdiff, maxval + valdiff];
            app.CONTRASTHIGHERSlider.Limits = limits;
            app.CONTRASTLOWERSlider.Limits = limits;

            % Change contrast value.
            app.CONTRASTHIGHERSlider.Value = maxval;
            app.CONTRASTLOWERSlider.Value = minval;
            app.contrast_high = max(max(img));
            app.contrast_low = min(min(img));
            app.show_frame();
        end
        
        function show_frame(app)
            % Display the timepoint.
            time = seconds(app.frame);
            time.Format = "hh:mm:ss";
            app.STARTTIMEEditField.Value = string(time);
            app.STARTFRAMEEditField.Value = sprintf("%d",app.frame);
            minmax = [app.contrast_low,app.contrast_high];
            img = app.calc_img(app.frame,app.COLORDropDown.Value, minmax);
            imshow(img, "Parent", app.UIAxes);
        end
        
        
        function img = calc_img(app, frame, colormap_name, minmax)
            minmax = round(minmax);

            % Choose colormap.
            switch colormap_name
                case "Gray"
                    colmap = gray(diff(minmax));
                case "Viridis"
                    colmap = begonia.colormaps.viridis(diff(minmax));
                case "Turbo"
                    colmap = turbo(diff(minmax));
                otherwise
                    error();
            end
            % Get frame.
            img1 = app.mat1(:,:,frame);
            % Adjust image contrast.
            img1 = img1 - minmax(1);
            img = ind2rgb(img1,colmap);
        end
    end
    

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

        % Code that executes after component creation
        function startupFcn(app, ts)
            app.ts = ts;
            app.frame = 1;
            app.CHANNELDropDown.Items = arrayfun(@(x) sprintf("%d",x), 1:ts.channels);
            limits = ceil(([1,ts.frame_count] - 1) * ts.dt);
            app.Slider.Limits = limits;
            app.Slider.MajorTicks = [];
            app.Slider.MinorTicks = [];
            if ts.has_var("trial_id")
                app.OUTPUTFILEEditField.Value = fullfile(get_project_path(), ...
                    "Plot", "TSeries mp4", ts.load_var("trial_id") + ".mp4");
            else
                app.OUTPUTFILEEditField.Value = "~/Desktop/example.mp4";
            end
            
            app.load_channel();
            app.show_frame();
        end

        % Value changing function: Slider
        function SliderValueChanging(app, event)
            frame = round(event.Value / app.ts.dt) + 1;
            frame = min(frame, app.ts.frame_count);
            app.frame = frame;
            app.show_frame();
        end

        % Value changed function: CHANNELDropDown
        function CHANNELDropDownValueChanged(app, event)
            app.load_channel();
            
        end

        % Value changed function: COLORDropDown
        function COLORDropDownValueChanged(app, event)
            app.show_frame();
        end

        % Button pushed function: Button_3
        function Button_3Pushed(app, event)
            app.frame = app.frame + 1;
            app.frame = min(app.frame,app.ts.frame_count);
            app.show_frame();
        end

        % Button pushed function: Button_4
        function Button_4Pushed(app, event)
            app.frame = app.frame + 5;
            app.frame = min(app.frame,app.ts.frame_count);
            app.show_frame();
        end

        % Button pushed function: Button_2
        function Button_2Pushed(app, event)
            app.frame = app.frame - 1;
            app.frame = max(app.frame,1);
            app.show_frame();
        end

        % Button pushed function: Button
        function ButtonPushed(app, event)
            app.frame = app.frame - 5;
            app.frame = max(app.frame,1);
            app.show_frame();
        end

        % Value changing function: CONTRASTHIGHERSlider
        function CONTRASTHIGHERSliderValueChanging(app, event)
            if event.Value <= app.contrast_low
                return;
            end
            app.contrast_high = event.Value;
            app.show_frame();
        end

        % Value changing function: CONTRASTLOWERSlider
        function CONTRASTLOWERSliderValueChanging(app, event)
            if event.Value >= app.contrast_high
                return;
            end
            app.contrast_low = event.Value;
            app.show_frame();
        end

        % Button pushed function: CREATEMP4Button
        function CREATEMP4ButtonPushed(app, event)
            outputfile = app.OUTPUTFILEEditField.Value;

            % Duration in frames.
            dur_frames = round(app.DURATONSECONDSEditField.Value / app.ts.dt);

            start_frame = app.frame;
            end_frame = app.frame + dur_frames;

            video_fps = app.MP4FPSEditField.Value;

            playback_speed = app.PLAYBACKSPEEDEditField.Value;

            num_frames = round(dur_frames / playback_speed);

            I = linspace(start_frame,end_frame,num_frames);
            I = round(I);

            colormap_name = app.COLORDropDown.Value;
            minmax = [app.contrast_low,app.contrast_high];

            begonia.path.make_dirs(outputfile);
            v = VideoWriter(outputfile,'MPEG-4');
            v.Quality = 100;
            v.FrameRate = video_fps;
            v.open();
            tic
            for i = I
                if i == I(1) || i == I(end) || toc > 5
                    fprintf("%d/%d\n", i, I(end));
                    tic;
                end
                img = app.calc_img(i,colormap_name,minmax);
                v.writeVideo(img);
            end
            v.close();

        end
    end

    % Component initialization
    methods (Access = private)

        % Create UIFigure and components
        function createComponents(app)

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

            % Create UIAxes
            app.UIAxes = uiaxes(app.UIFigure);
            app.UIAxes.Position = [2 205 1036 826];

            % Create Button
            app.Button = uibutton(app.UIFigure, 'push');
            app.Button.ButtonPushedFcn = createCallbackFcn(app, @ButtonPushed, true);
            app.Button.Position = [20 120 100 22];
            app.Button.Text = '<<';

            % Create Button_2
            app.Button_2 = uibutton(app.UIFigure, 'push');
            app.Button_2.ButtonPushedFcn = createCallbackFcn(app, @Button_2Pushed, true);
            app.Button_2.Position = [224 120 100 22];
            app.Button_2.Text = '<';

            % Create Button_3
            app.Button_3 = uibutton(app.UIFigure, 'push');
            app.Button_3.ButtonPushedFcn = createCallbackFcn(app, @Button_3Pushed, true);
            app.Button_3.Position = [723 120 100 22];
            app.Button_3.Text = '>';

            % Create Button_4
            app.Button_4 = uibutton(app.UIFigure, 'push');
            app.Button_4.ButtonPushedFcn = createCallbackFcn(app, @Button_4Pushed, true);
            app.Button_4.Position = [921 120 100 22];
            app.Button_4.Text = '>>';

            % Create Slider
            app.Slider = uislider(app.UIFigure);
            app.Slider.MajorTicks = [];
            app.Slider.MajorTickLabels = {''};
            app.Slider.ValueChangingFcn = createCallbackFcn(app, @SliderValueChanging, true);
            app.Slider.MinorTicks = [];
            app.Slider.Position = [24 90 996 3];

            % Create MP4FPSEditFieldLabel
            app.MP4FPSEditFieldLabel = uilabel(app.UIFigure);
            app.MP4FPSEditFieldLabel.HorizontalAlignment = 'right';
            app.MP4FPSEditFieldLabel.Position = [20 17 56 22];
            app.MP4FPSEditFieldLabel.Text = 'MP4 FPS';

            % Create DURATONSECONDSEditFieldLabel
            app.DURATONSECONDSEditFieldLabel = uilabel(app.UIFigure);
            app.DURATONSECONDSEditFieldLabel.HorizontalAlignment = 'right';
            app.DURATONSECONDSEditFieldLabel.Position = [417 17 130 22];
            app.DURATONSECONDSEditFieldLabel.Text = 'DURATON (SECONDS)';

            % Create DURATONSECONDSEditField
            app.DURATONSECONDSEditField = uieditfield(app.UIFigure, 'numeric');
            app.DURATONSECONDSEditField.Limits = [0 Inf];
            app.DURATONSECONDSEditField.Position = [562 17 100 22];
            app.DURATONSECONDSEditField.Value = 10;

            % Create MP4FPSEditField
            app.MP4FPSEditField = uieditfield(app.UIFigure, 'numeric');
            app.MP4FPSEditField.Position = [91 17 100 22];
            app.MP4FPSEditField.Value = 30;

            % Create CREATEMP4Button
            app.CREATEMP4Button = uibutton(app.UIFigure, 'push');
            app.CREATEMP4Button.ButtonPushedFcn = createCallbackFcn(app, @CREATEMP4ButtonPushed, true);
            app.CREATEMP4Button.Position = [922 17 100 22];
            app.CREATEMP4Button.Text = 'CREATE MP4';

            % Create CHANNELDropDownLabel
            app.CHANNELDropDownLabel = uilabel(app.UIFigure);
            app.CHANNELDropDownLabel.HorizontalAlignment = 'right';
            app.CHANNELDropDownLabel.Position = [24 167 62 22];
            app.CHANNELDropDownLabel.Text = 'CHANNEL';

            % Create CHANNELDropDown
            app.CHANNELDropDown = uidropdown(app.UIFigure);
            app.CHANNELDropDown.Items = {'Gray', 'Viridis', 'Turbo'};
            app.CHANNELDropDown.ValueChangedFcn = createCallbackFcn(app, @CHANNELDropDownValueChanged, true);
            app.CHANNELDropDown.Position = [101 167 100 22];
            app.CHANNELDropDown.Value = 'Gray';

            % Create CONTRASTLOWERSliderLabel
            app.CONTRASTLOWERSliderLabel = uilabel(app.UIFigure);
            app.CONTRASTLOWERSliderLabel.HorizontalAlignment = 'right';
            app.CONTRASTLOWERSliderLabel.Position = [221 170 115 22];
            app.CONTRASTLOWERSliderLabel.Text = 'CONTRAST LOWER';

            % Create CONTRASTLOWERSlider
            app.CONTRASTLOWERSlider = uislider(app.UIFigure);
            app.CONTRASTLOWERSlider.ValueChangingFcn = createCallbackFcn(app, @CONTRASTLOWERSliderValueChanging, true);
            app.CONTRASTLOWERSlider.Position = [357 189 150 3];

            % Create CONTRASTHIGHERSliderLabel
            app.CONTRASTHIGHERSliderLabel = uilabel(app.UIFigure);
            app.CONTRASTHIGHERSliderLabel.HorizontalAlignment = 'right';
            app.CONTRASTHIGHERSliderLabel.Position = [538 170 118 22];
            app.CONTRASTHIGHERSliderLabel.Text = 'CONTRAST HIGHER';

            % Create CONTRASTHIGHERSlider
            app.CONTRASTHIGHERSlider = uislider(app.UIFigure);
            app.CONTRASTHIGHERSlider.ValueChangingFcn = createCallbackFcn(app, @CONTRASTHIGHERSliderValueChanging, true);
            app.CONTRASTHIGHERSlider.Position = [677 189 150 3];

            % Create COLORDropDownLabel
            app.COLORDropDownLabel = uilabel(app.UIFigure);
            app.COLORDropDownLabel.HorizontalAlignment = 'right';
            app.COLORDropDownLabel.Position = [858 167 47 22];
            app.COLORDropDownLabel.Text = 'COLOR';

            % Create COLORDropDown
            app.COLORDropDown = uidropdown(app.UIFigure);
            app.COLORDropDown.Items = {'Gray', 'Viridis', 'Turbo'};
            app.COLORDropDown.ValueChangedFcn = createCallbackFcn(app, @COLORDropDownValueChanged, true);
            app.COLORDropDown.Position = [920 167 100 22];
            app.COLORDropDown.Value = 'Gray';

            % Create STARTTIMEEditFieldLabel
            app.STARTTIMEEditFieldLabel = uilabel(app.UIFigure);
            app.STARTTIMEEditFieldLabel.HorizontalAlignment = 'right';
            app.STARTTIMEEditFieldLabel.Enable = 'off';
            app.STARTTIMEEditFieldLabel.Position = [210 17 73 22];
            app.STARTTIMEEditFieldLabel.Text = 'START TIME';

            % Create STARTTIMEEditField
            app.STARTTIMEEditField = uieditfield(app.UIFigure, 'text');
            app.STARTTIMEEditField.Editable = 'off';
            app.STARTTIMEEditField.Enable = 'off';
            app.STARTTIMEEditField.Position = [298 17 100 22];

            % Create STARTFRAMEEditFieldLabel
            app.STARTFRAMEEditFieldLabel = uilabel(app.UIFigure);
            app.STARTFRAMEEditFieldLabel.HorizontalAlignment = 'right';
            app.STARTFRAMEEditFieldLabel.Enable = 'off';
            app.STARTFRAMEEditFieldLabel.Position = [423 120 86 22];
            app.STARTFRAMEEditFieldLabel.Text = 'START FRAME';

            % Create STARTFRAMEEditField
            app.STARTFRAMEEditField = uieditfield(app.UIFigure, 'text');
            app.STARTFRAMEEditField.Editable = 'off';
            app.STARTFRAMEEditField.Enable = 'off';
            app.STARTFRAMEEditField.Position = [524 120 100 22];

            % Create PLAYBACKSPEEDEditFieldLabel
            app.PLAYBACKSPEEDEditFieldLabel = uilabel(app.UIFigure);
            app.PLAYBACKSPEEDEditFieldLabel.HorizontalAlignment = 'right';
            app.PLAYBACKSPEEDEditFieldLabel.Position = [680 17 109 22];
            app.PLAYBACKSPEEDEditFieldLabel.Text = 'PLAYBACK SPEED';

            % Create PLAYBACKSPEEDEditField
            app.PLAYBACKSPEEDEditField = uieditfield(app.UIFigure, 'numeric');
            app.PLAYBACKSPEEDEditField.Limits = [0 Inf];
            app.PLAYBACKSPEEDEditField.Position = [804 17 100 22];
            app.PLAYBACKSPEEDEditField.Value = 1;

            % Create OUTPUTFILEEditField
            app.OUTPUTFILEEditField = uieditfield(app.UIFigure, 'text');
            app.OUTPUTFILEEditField.Editable = 'off';
            app.OUTPUTFILEEditField.Enable = 'off';
            app.OUTPUTFILEEditField.Position = [136 50 884 22];

            % Create OUTPUTFILEButton
            app.OUTPUTFILEButton = uibutton(app.UIFigure, 'push');
            app.OUTPUTFILEButton.Position = [20 50 100 22];
            app.OUTPUTFILEButton.Text = 'OUTPUT FILE';

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

    % App creation and deletion
    methods (Access = public)

        % Construct app
        function app = Mp4Maker(varargin)

            % Create UIFigure and components
            createComponents(app)

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

            % 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.UIFigure)
        end
    end
end