classdef polariMBGrid < internal.polariMouseBehavior
    
    methods
        function obj = polariMBGrid(isButtonDown)
            
            if nargin > 0 && isButtonDown
                obj.InstallFcn = @showToolTipAndPtr_buttondown;
                obj.MotionEvent = @wbdrag;
                obj.DownEvent = [];
                obj.UpEvent = @wbup;
                obj.ScrollEvent = [];
            else
                obj.InstallFcn = @internal.polariMBGrid.showToolTipAndPtr;
                obj.MotionEvent = @wbmotion;
                obj.DownEvent = @internal.polariMBGrid.wbdown;
                obj.UpEvent = [];
                obj.ScrollEvent = @internal.polariMBMagTicks.wbscroll;
            end
        end
    end
    
    methods (Static)
        function wbdown(p,ev)
            % Clicked on grid.
            %
            % Begin change in zoom, or add cursor.
            
            st = p.hFigure.SelectionType;
            p.hFigure.CurrentPoint = ev.Point;
            %resetToolTip(p);
            
            if strcmpi(st,'open')
                % double-click on grid: add cursor
                %
                % Figure out where the mouse is -- NOT what fig/axes is
                % associated with p.  That may be different.
                thisFig = ev.Source;
                thisAxes = thisFig.CurrentAxes;
                if p.pAxesIndex == getappdata(thisAxes,'PolariAxesIndex')
                    % Double-click within grid region
                    %
                    % If this polari instance is rendered in the current
                    % axis, continue processing the mouse click:
                    m_addCursor(p);
                    
                    % There's a chance the cursor is over a newly visible
                    % span, if it just appeared, instead of the grid.  The
                    % nearest data point with the new cursor may be close
                    % or far from the cursor now.
                    s = computeHoverLocation(p,ev);
                    if s.overGrid
                        % Mouse handler continues in grid motion state,
                        % with button pressed and ChangedState=false.
                        % reinstate grid ToolTips since they only
                        % install at first grid motion. (We may not have
                        % motion after the double-click hence the user may
                        % never get the ToolTip without this.)
                        
                        % We update the tooltip whenever the mouse crosses the
                        % "threshold" from min to max radius:
                        pt = p.hAxes.CurrentPoint(1,1:2);
                        ch = norm(pt) <= getMagTickHoverBrkpt(p);
                        internal.polariMBGrid.showToolTipAndPtr(p,ch);
                    else
                        autoChangeMouseBehavior(p,s);
                    end
                end
                return
            elseif strcmpi(st,'alt')
                % right-click: this action launches the context menu.
                %
                % Prevent further reaction to right-click here, so we don't
                % do any hilite and confuse the user when the context menu
                % launches.
                resetToolTip(p);
                return
            end
            
            % Install mouse pointer icon
            %setptr(p.hFigure,'uddrag');
            
            % Get mouse coords before other work.
            % Need axes in normalized units for drag.
            ax = p.hAxes;
            ax.Units = 'normalized';
            pt = ax.CurrentPoint;
            
            % Clicked on grid for this polari instance.
            % Get position, units are pixels for this uicontrol:
            pt = pt(1,1:2);
            r = norm(pt);
            %ci.OrigPt = pt;
            ci.ChangedMagnitudeLim = false;
            
            % This property changes the 4-arrow hilite to 2-arrow, which
            % we do right from the start for grid based zoom:
            p.MagDrag_ChangedMagnitudeLim = true;
            p.MagDrag_OrigMagnitudeLim = p.pMagnitudeLim;
            
            % Disable mouse in all other instances of polari within
            % this figure
            [ci.OtherPolariInstances, ci.OtherPolariPrevEna] = ...
                enableMouseInOtherInstances(p,false);
            
            % Reuse the cache for span readout, since we're mutually
            % exclusive with that:
            p.SpanReadout_CacheInfo = ci;
            
            % To do "mag line endpoint pull", we can't use the unit-circle
            % origin as reference.  We use the point clicked as origin, and
            % two points equidistant from it that are along the radial line
            % formed between ORIGIN and FIRST CLICKED POINT on grid: one
            % far away along the -ve direction, and one far away along the
            % +ve direction.
            %
            % As the drag continues, we compare the current (x,y) point to
            % these two points, and depending on which one we're closer to,
            % we incr or decr the lim(1) or lim(2) point.  Note that lim(1)
            % vs lim(2) depends solely on where we first clicked.
            %
            % Capture the points:
            lineAngNorm = atan2(pt(2),pt(1));
            s.InitialPointClicked = pt;
            s.LineUnitVecCol = [cos(lineAngNorm); sin(lineAngNorm)];
            s.ChangingLowerLim = r <= getMagTickHoverBrkpt(p);
            p.MagDrag_MouseDown = s;
            
            hideAngleMarkerDataDots(p,true);
            
            % Show 2-arrow hilights right away
            if p.MagnitudeTickLabelVisible
                hiliteMagAxisDrag_Init(p,'on');
            end
            
            if s.ChangingLowerLim
                action = 'lower';
            else
                action = 'upper';
            end
            
            if p.MagnitudeTickLabelVisible
                hiliteMagAxisDrag_Init(p,'update',action);
            end
            
            changeMouseBehavior(p,'grid_buttondown');
        end
        
        function showToolTipAndPtr(p,changingLowerLim)
            if p.ToolTips && ~isempty(p.hToolTip) && isvalid(p.hToolTip)
                
                setptr(p.hFigure,'arrow');
                
                if nargin < 2
                    % no insight as to upper/lower limit
                    str = 'Drag to change magnitude limits';
                    
                elseif changingLowerLim
                    str = 'Drag to change LOWER magnitude limit';
                    %str = 'Drag radially for minimum limit';
                    %str = 'Drag radially for detail';
                else
                    str = 'Drag to change UPPER magnitude limit';
                    %str = 'Drag radially for maximum limit';
                    %str = 'Drag radially for zoom';
                end
                %str = {'DISPLAY',str,''};
                
                N = numel(p.hPeakAngleMarkers) + ...
                    numel(p.hCursorAngleMarkers);
                if N ~= 1 || ~p.Span
                    % If no cursors, or 2 or more, don't talk about adding
                    % a span.  If there's just 1 cursor, and the span
                    % option is on, guide user appropriately:
                    str = {str,'Double-click to add cursor'};
                else
                    str = {str, 'Double-click to show span'};
                end
                start(p.hToolTip,str);
            end
        end
    end
end

function wbdrag(p,ev)
% Drag grid
%  - graphical updates to zoom

p.hFigure.CurrentPoint = ev.Point;
pt = p.hAxes.CurrentPoint(1,1:2);

% Find distance of mouse-drag, from initial click, based on
% direction of mag-angle line.
%
% Do a vector dot-product to project point onto mag-line,
% giving normalized (unit-radius) distance traveled along
% mag-line:
s = p.MagDrag_MouseDown;
ptLocalRow = pt - s.InitialPointClicked;
normdist = ptLocalRow * s.LineUnitVecCol;  % inner (dot) product

% Find radial distance traveled along drag-line
origLim = p.MagDrag_OrigMagnitudeLim; % unscaled
origLimDiff = origLim(2) - origLim(1);
limChange = normdist * origLimDiff;

newLim = origLim;
%if changing_min
if s.ChangingLowerLim
    newLim(1) = newLim(1) - limChange;
else
    newLim(2) = newLim(2) - limChange;
end

if ~isequal(newLim, p.pMagnitudeLim)
    % Operations to do once per down-drag-up sequence
    ci = p.SpanReadout_CacheInfo;
    if ~ci.ChangedMagnitudeLim
        ci.ChangedMagnitudeLim = true;
        p.SpanReadout_CacheInfo = ci;
        
        enableListeners(p,false);
        p.MagnitudeLimMode = 'manual'; % AbortSet makes this efficient
        enableListeners(p,true);
    end
    mvis = internal.LogicalToOnOff(p.MagnitudeTickLabelVisible);
    updateMagnitudeLim(p,newLim,mvis); % xxx pass optional 3rd arg for vis
    %updateMagnitudeLim(p,newLim); % xxx pass optional 3rd arg for vis
    
    return % EARLY EXIT
end
end

function wbup(p,ev)

hiliteMagAxisDrag_Init(p,'off');

% Set point into axes:
p.hFigure.CurrentPoint = ev.Point;

% Enable mouse in all other instances of polari within
% this figure
%
% Get list of other instances from cache before clearing it.
% Removes need to recompute list.
%
% We're reusing span readout cache for grid zoom cache
ci = p.SpanReadout_CacheInfo;
enableMouseInOtherInstances(p,true, ...
    ci.OtherPolariInstances, ...
    ci.OtherPolariPrevEna);

% Clear out drag-cache, so we release marker handles etc
p.SpanReadout_CacheInfo = [];
p.MagDrag_ChangedMagnitudeLim = false;

hideAngleMarkerDataDots(p,false);

% Update mouse handlers, and kick it once manually for mouse
% pointer updates etc:
%
% Check where mouse is to determine next step:
s = computeHoverLocation(p,ev);
if s.overGrid || s.overLobes
    % Treat lobes like grid, since there is no specific handler
    % for it
    changeMouseBehavior(p,'grid');
    
    if s.overLobes
        if p.ToolTips && ~isempty(p.hToolTip) && isvalid(p.hToolTip)
            start(p.hToolTip, ...
                {upper([s.overLobesType ' Lobe']), ...
                'Right-click for options'});
        end
    else
        % We update the tooltip whenever the mouse crosses the
        % "threshold" from min to max radius:
        pt = p.hAxes.CurrentPoint(1,1:2);
        ch = norm(pt) <= getMagTickHoverBrkpt(p);
        internal.polariMBGrid.showToolTipAndPtr(p,ch);
    end
else
    % No longer hovering over grid
    autoChangeMouseBehavior(p,s);
end
end

function wbmotion(p,ev)

s = computeHoverLocation(p,ev);
firstTime = p.ChangedState;

if firstTime || s.overGrid
    pt = p.hAxes.CurrentPoint(1,1:2);
    ch = norm(pt) <= getMagTickHoverBrkpt(p);
end

if firstTime
    p.ChangedState = false;
    p.pGridMotion_ChangingLowerLim = -1;
    internal.polariMBGrid.showToolTipAndPtr(p,ch);
end

if firstTime || s.overGrid
    % We update the tooltip whenever the mouse crosses the
    % "threshold" from min to max radius:
    if p.pGridMotion_ChangingLowerLim ~= ch
        p.pGridMotion_ChangingLowerLim = ch;
        internal.polariMBGrid.showToolTipAndPtr(p,ch);
    end
    
else
    % Not over grid (possibly not over axes)
    %resetToolTip(p);
    
    % In case invis mag that we made appear:
    overrideMagnitudeTickLabelVis(p,'default');
    
    %autoChangeMouseBehavior(p,s);
    internal.polariMBGeneral.wbmotion(p,ev); % execute deferred operations
    
    p.pGridMotion_ChangingLowerLim = -1;
end
end

function showToolTipAndPtr_buttondown(p)
if p.ToolTips && ~isempty(p.hToolTip) && isvalid(p.hToolTip)
    s = p.MagDrag_MouseDown;
    if s.ChangingLowerLim
        str = 'Drag to change LOWER magnitude limit';
    else
        str = 'Drag to change UPPER magnitude limit';
    end
    start(p.hToolTip,str);
    setptr(p.hFigure,'closedhand');
end
end
