classdef HelperHDLMUI < handle
    %helperHDLMUI Helper class to display HERE HD Live Map data with video.
    
    %   Copyright 2018 The MathWorks, Inc.
    
    properties (SetAccess = private)
        Figure
        MapPlayer
        Image
    end
    
    properties (Access = private)
        ImageAxes
        
        TimestampData
        LaneAxes
        SpeedData
        
        LinkPlot
        LaneGroupPlot
    end
    
    methods
        function this = HelperHDLMUI(lat, lon)
            % Class constructor.
            
            imageWidth  = 640;
            imageHeight = 480;
            margin      = 10;
            
            mapWidth    = 400;
            mapHeight   = 300;
            
            panelTitleSize = 10;
            
            halfPanelWidth  = mapWidth/2 - margin/2;
            halfPanelHeight = imageHeight - mapHeight - 100 - 2*margin;
            
            % Create figure container
            this.Figure = figure( ...
                'Name',             'HERE HD Live Map Example', ...
                'Toolbar',          'none', ...
                'MenuBar',          'none', ...
                'NumberTitle',      'off', ...
                'InvertHardcopy',	'off');
            
            this.Figure.Position(3:4) = [imageWidth + mapWidth + 3*margin, ...
                imageHeight + 2*margin];
            
            % Create image panel for video stream
            this.ImageAxes = axes(this.Figure, ...
                'Units',                    'pixel', ...
                'Position',                 [margin, margin, imageWidth, imageHeight], ...
                'YDir',                     'reverse',...
                'TickDir',                  'out', ...
                'XGrid',                    'off', ...
                'YGrid',                    'off', ...
                'PlotBoxAspectRatioMode',   'auto');
            
            this.Image = image([1, imageWidth], [1, imageHeight], ...
                zeros(imageWidth, imageHeight, 3, 'uint8'), ...
                'BusyAction',       'cancel', ...
                'Parent',           this.ImageAxes, ...
                'CDataMapping',     'direct', ...
                'Interruptible',    'off');
            
            this.ImageAxes.Visible = 'off';
            
            % Create map panel and geoplayer for GPS data
            mapPanelPosition = [...
                imageWidth + 2*margin, ...
                imageHeight - mapHeight + margin, ...
                mapWidth, ...
                mapHeight];
            
            hMapPanel = uipanel(this.Figure, ...
                'Units',        'pixel', ...
                'BorderType',   'none', ...
                'Position',     mapPanelPosition);
            
            zoomLevel = 18;
            this.MapPlayer = geoplayer(lat, lon, zoomLevel, ...
                'HistoryDepth', Inf, ...
                'HistoryStyle', 'line', ...
                'Parent',       hMapPanel);
            
            % Create line objects to display the link.
            this.LinkPlot = geoplot(this.MapPlayer.Axes, NaN, NaN, 'r.-', ...
                'DisplayName', 'Link');
            
            % Create line and text objects to display the link.
            this.LaneGroupPlot = geoplot(this.MapPlayer.Axes, NaN, NaN, 'm.-', ...
                'DisplayName', 'Lane Group');
            
            legend([this.LinkPlot, this.LaneGroupPlot], ...
                'Location',     'Northwest', ...
                'AutoUpdate',   'off');
            
            % Create panel for timestamp
            timeStampPanelPosition = [...
                imageWidth + 2*margin, ...
                100 + 2*margin, ...
                halfPanelWidth, ...
                halfPanelHeight];
            
            hTimestampPanel = uipanel(this.Figure, ...
                'Units',            'pixel', ...
                'Title',            'Timestamp', ...
                'FontSize',         panelTitleSize, ...
                'Position',         timeStampPanelPosition);
            
            this.TimestampData = uicontrol(hTimestampPanel, ...
                'Style',                'text', ...
                'String',               '--', ...
                'HorizontalAlignment',  'center', ...
                'FontSize',             18, ...
                'Units',                'normalized', ...
                'Position',             [0 0 1 0.9]);
            
            % Create panel for speed data
            speedPanelPosition = [...
                imageWidth + 2.5*margin + mapWidth/2, ...
                100 +  2*margin, ...
                halfPanelWidth, ...
                halfPanelHeight];
            
            hSpeedPanel = uipanel(this.Figure, ...
                'Units',            'pixel', ...
                'Title',            'Speed Limit', ...
                'FontSize',         panelTitleSize, ...
                'Position',         speedPanelPosition);
            
            this.SpeedData = uicontrol(hSpeedPanel, ...
                'Style',    'text', ...
                'String',   '--', ...
                'FontSize', 18, ...
                'Units',    'normalized', ...
                'Position', [0 0 1 0.9]);
            
            % Create panel for plotting lanes
            lanePanelPosition = [imageWidth + 2*margin, margin, mapWidth, 100];
            
            hLanePanel = uipanel(this.Figure, ...
                'Units',            'pixel', ...
                'Title',            'Lane Types and Boundaries', ...
                'FontSize',         panelTitleSize, ...
                'Position',         lanePanelPosition);
            
            this.LaneAxes = axes(hLanePanel, ...
                'Color',        [0.65 0.65 0.65], ...
                'XLim',         [0 5], ...
                'YLim',         [0 1], ...
                'XLimMode',     'manual', ...
                'YLimMode',     'manual', ...
                'XTickMode',    'manual', ...
                'YTickMode',    'manual', ...
                'Box',          'on', ...
                'Position',     [0.03 0.1 0.94 0.8]);
            
            this.LaneAxes.Toolbar.Visible = 'off';
            
            disableDefaultInteractivity(this.LaneAxes);
            
            movegui(this.Figure, 'center');
        end
        
        function updateTime(this, time)
            %updateTime Update the timestamp
            time.Format = 'HH:mm:ss';
            this.TimestampData.String = char(time);
        end
        
        function updatePosition(this, lat, lon)
            %updatePosition Update the latitude-longitude position on map
            this.MapPlayer.plotPosition(lat, lon);
        end
        
        function updateLink(this, linkLat, linkLon)
            %updateLink Update the plot latitude-longitude data
            
            if ishghandle(this.LinkPlot)
                set(this.LinkPlot, ...
                    'LatitudeData', linkLat, ...
                    'LongitudeData', linkLon);
            end
        end
        
        function updateLaneGroup(this, boundary)
            %updateLaneGroup Update the plot latitude-longitude data
            
            if ishghandle(this.LaneGroupPlot)
                set(this.LaneGroupPlot, ...
                    'LatitudeData',  boundary(:,1), ...
                    'LongitudeData', boundary(:,2));
            end
        end
        
        function updateImage(this, image)
            %updateImage Update display with new image
            this.Image.CData = image;
            this.ImageAxes.Visible = 'off';
            drawnow limitrate;
        end
        
        function updateSpeed(this, speed)
            %updateSpeed Update the display with new speed value
            this.SpeedData.String = num2str(speed);
        end
        
        function plotLanes(this, laneTypes, boundaries, isForward)
            %plotLane Update lane axes with lane boundaries and types
            
            % Reset
            delete(this.LaneAxes.Children);
            
            if numel(laneTypes) > 1
                
                numBoundaries = numel(boundaries);
                sep = 5 / numBoundaries;
                
                nonEmptyBoundaries = find(~cellfun(@isempty, boundaries));
                
                if ~isForward
                    laneTypes = fliplr(laneTypes);
                    nonEmptyBoundaries = fliplr(nonEmptyBoundaries);
                end
                
                % Plot each lane boundary
                idx = 1;
                for boundId = nonEmptyBoundaries
                    elem = boundaries{boundId}.Elements;
                    switch elem.Style
                        case 'SOLID'
                            lineStyle = '-';
                        case 'DASHED'
                            lineStyle = '--';
                        case 'ALTERNATE_DASHED'
                            lineStyle = ':';
                        otherwise
                            lineStyle = 'none';
                    end
                    
                    xline(this.LaneAxes, ...
                        sep*(idx-0.5), ...
                        'Color', lower(elem.Color), ...
                        'LineStyle', lineStyle, ...
                        'Alpha', 1, ...
                        'LineWidth', 2, ...
                        'LabelHorizontalAlignment', 'center', ...
                        'LabelVerticalAlignment', 'middle');
                    idx = idx + 1;
                end
                
                numLanes = numel(laneTypes);
                sep = 5 / (numLanes+1);
                
                % Plot each lane type
                for idx = 1:numLanes                    
                    switch laneTypes{idx}
                        case 'REGULAR'
                            text(this.LaneAxes, ...
                                sep * idx, 0.5, '\uparrow', ...
                                'HorizontalAlignment', 'center', ...
                                'FontSize', 30, ...
                                'Color', 'white', ...
                                'FontWeight', 'bold');
                        case 'TURN'
                            if idx < numLanes / 2
                                arw = '\leftarrow';
                            else
                                arw = '\rightarrow';
                            end
                            text(this.LaneAxes, ...
                                sep * idx, 0.5, arw, ...
                                'HorizontalAlignment', 'center', ...
                                'fontsize', 30, ...
                                'Color', 'white');
                        otherwise
                            text(this.LaneAxes, ...
                                sep * idx, 0.5, laneTypes{idx}, ...
                                'HorizontalAlignment', 'center', ...
                                'Color', 'white', ...
                                'FontWeight', 'bold', ...
                                'Rotation', 90);
                    end                    
                end
            end
            
        end
        
    end
end