## Copyright (C) 2016 Pantxo Diribarne
## 
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
## 
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*- 
## @deftypefn {Function File} {@var{retval} =} impixelinfoval (@var{input1}, @var{input2})
##
## @seealso{}
## @end deftypefn

## Author: Pantxo Diribarne <pantxo@gre046552>
## Created: 2016-09-07

function hc = impixelinfoval (hp = [], him = [])
  
  tk = graphics_toolkit ();
  if (! strcmp (tk, "qt"))
    error ("impixelinfoval: not implemented for %s toolkit", tk)
  endif
  
  if (isempty (hp) || isempty (him))
    print_usage ();
  elseif (! ishandle (hp) || ! all (ishandle (him)))
    error ("impixelinfoval: expect HP and HIM to be valid graphics handles")
  elseif (! isfigure (hp) && ! strcmp (get (hp, "type"), "uipanel"))
    error ("impixelinfoval: expect parent to be a figure or a uipanel")
  elseif (! all (strcmp (get (him, "type"), "image")))
    error ("impixelinfoval: expect HIM to be an (array of) image handle(s)");
  endif
    
  hf = ancestor (him(1), "figure");
  update_data (hf, [], him);
  handle_listeners ("add", him);
  
  hc = uicontrol (hp, "style", "text", "units", "normalized", ...
                  "position", [0 0 1 0.05], "horizontalalignment", "left", ...
                  "deletefcn", {@hcdelete, him});

  set (hf, "windowbuttonmotionfcn", {@motionfcn, hc});
endfunction

function update_data (h, d, him = [])
  hf = ancestor (h, "figure");
  if (isempty (him))
    data = getappdata (hf, "__impixelinfodata__");
    him = [data.him];
    data = [];
  endif
  him(! ishandle (him)) = [];
  nim = numel (him);
  ## Cache image informations into the appdata of the figure
  for ii = 1:nim
    data(ii).him = him(ii);
    data(ii).coordfcn = get_coord_fcn (him(ii));
  endfor
  setappdata (hf, "__impixelinfodata__", data);  
endfunction

function handle_listeners (action, him)
  persistent lsn = @update_data;
  persistent oldhax = [];
  persistent hf = ancestor (him(1), "figure");
  
  
  fcn = ifelse (strcmp (action, "add"), @addlistener, @dellistener);
  
  ## Figure
  hf = ancestor (him(1), "figure");
  feval (fcn, hf, "position", lsn);

  ## Images  
  him(! ishandle (him)) = [];
  for ii = 1:numel (him)
    ## Store axes
    oldhax = [oldhax ancestor(him(ii), "axes")];
    
    feval (fcn, him(ii), "xdata", lsn);
    feval (fcn, him(ii), "ydata", lsn);
    feval (fcn, him(ii), "cdata", lsn);
    feval (fcn, him(ii), "cdatamapping", lsn);
  endfor

  oldhax = unique (oldhax(ishandle (oldhax)));
  for hax = oldhax
    feval (fcn, hax, "position", lsn);
  endfor
endfunction

function hcdelete (h, d, him)
  set (gcbf (), "windowbuttonmotionfcn", []);
  handle_listeners ("remove", him);
endfunction

function motionfcn (h,d, hc)
  hasna = 1;
  data = getappdata (h, "__impixelinfodata__");
  pt = get (h, "currentpoint");
  for ii = 1:numel (data)
    [str, hasnan] = data(ii).coordfcn (pt);
    if (! hasnan)
      break
    endif
  endfor
  set (hc, "string", str)
endfunction

function fcn = get_coord_fcn (him)
  xdata = get (him, "xdata");
  ydata = get (him, "ydata");
  cdata = get (him, "cdata");
  if (xdata(1) > xdata(2))
    cdata = fliplr (cdata);
    xdata = xdata(end:-1:1);
  endif
  if (ydata(1) > ydata(2))
    cdata = flipud (cdata);
    ydata = ydata(end:-1:1);
  endif
  
  mapping = get (him, "cdatamapping");
  
  hax = ancestor (him, "axes");
  xl = get (hax, "xlim");
  yl = get (hax, "ylim");
  cmap = colormap (hax);
  
  unwind_protect
    units = get (hax, "units");
    set (hax, "units", "pixels");
    axpos = get (hax, "position");
  unwind_protect_cleanup
    set (hax, "units", units);
  end_unwind_protect

  ## We assume "ydir" is "reverse"
  axpos(2) += axpos(4);
  axpos(4) *= -1;
  tmp = @(xy) (xy(:)'-axpos(1:2)) ./ axpos(3:4) .* [diff(xl) diff(yl)] + ...
      [xl(1) yl(1)];
  pxy = __image_pixel_size__ (him);
  xdata(1) -= pxy(1);
  xdata(2) += pxy(1);
  ydata(1) -= pxy(2);
  ydata(2) += pxy(2);
  fcn = @(xy) pixinfo (xy, tmp, xdata, ydata, cdata, pxy, mapping, cmap);
endfunction

function [str, hasna] = pixinfo (xy, fig2axfcn, xd, yd, cdata, pxy,...
                                 mapping, cmap)
  str = "";
  
  xy = fig2axfcn (xy);
  nx = columns (cdata);
  ny = rows (cdata);
  
  z = NA;
  hasna = true;
  if (all ((xy > [xd(1) yd(1)]) & (xy < [xd(2) yd(2)])))
    hasna = false;
    idx(1) = sum (xy(1) > [linspace(xd(1), xd(2)-pxy(1)*2, nx)]);
    idx(2) = sum (xy(2) > [linspace(yd(1), yd(2)-pxy(2)*2, ny)]);
    z = cdata(idx(2), idx(1), :);

    if (ndims (cdata) == 3)
      ## True color
      str = sprintf ("(%d, %d) [%3.2f, %3.2f, %3.2f])", ...
                     round (xy(1)), round (xy(2)), z(1), z(2), z(3));      
    elseif (strcmp (mapping, "direct"))
      ncol = rows (cmap);
      idx = round (z);
      idx(z<1) = 1;
      idx(z>ncol) = ncol;
      rgb = cmap (idx,:);
      if (ismember (class (cdata), {"double", "single"}))
        str = sprintf ("(%d, %d) %3.2f <%d> [%3.2f, %3.2f, %3.2f]",...
                       round (xy(1)), round (xy(2)), z, idx, ...
                       rgb(1), rgb(2), rgb(3));
      else
        str = sprintf ("(%d, %d) <%d> [%3.2f, %3.2f, %3.2f]",
                       round (xy(1)), round (xy(2)), z, ...
                       rgb(1), rgb(2), rgb(3));
      endif
    else
      str = sprintf ("(%d, %d) %g", round (xy(1)), round (xy(2)), z);
    endif
  endif
  if (isempty (str))
    str = "(X, Y) Pixel Value";
  endif
endfunction