function hfigure = imageinfo(varargin)
%IMAGEINFO Image information tool.
%   IMAGEINFO creates an Image Information tool associated with the image
%   in the current figure. The tool displays information about the basic
%   attributes of the target image in a separate figure. The information 
%   includes:
%
%      Width (columns)
%      Height (rows)
%      Class
%      Image type
%      Minimum intensity/index (only for 'intensity' or 'indexed' images)
%      Maximum intensity/index (only for 'intensity' or 'indexed' images)
%
%   IMAGEINFO(H) creates an Image Information tool associated with H, where
%   H is a handle to a figure, axes, or image object.
%
%   IMAGEINFO(FILENAME) creates an Image Information tool containing image
%   metadata from the graphics file FILENAME. The image does not have to be
%   displayed in a figure window. FILENAME can be any file type that has been
%   registered with an information function in the file formats registry,
%   IMFORMATS, so its information can be read by IMFINFO. FILENAME can also be a
%   DICOM file with information readable by DICOMINFO.
%
%   IMAGEINFO(INFO) creates an Image Information tool containing image metadata
%   in the structure INFO. INFO is a structure returned by the functions IMFINFO
%   or DICOMINFO, or INFO can be a user-created structure.
%
%   IMAGEINFO(HIMAGE,FILENAME) creates an Image Information tool containing
%   information about the basic attributes of the image specified by the
%   handle HIMAGE and the image metadata from the graphics file FILENAME.
%
%   IMAGEINFO(HIMAGE,INFO) creates an Image Information tool containing
%   information about the basic attributes of the image specified by the
%   handle HIMAGE and the image metadata in the structure INFO.
%
%   HFIGURE = IMAGEINFO(...) returns a handle to the Image Information
%   tool figure.
%
%   Note
%   ----
%   The Image Information tool gets information about image attributes
%   by querying the image object's CData. The image object converts the
%   CData for a single or int16 images to class double. In these cases,
%   IMAGEINFO(H) returns a 'Class' of 'double', even though the image
%   is of class single or int16. For example,
%
%       h = imshow(ones(10,'int16'));
%       class(get(h,'CData')) 
%
%   Examples
%   --------
%
%       imageinfo('peppers.png');
%
%       h = imshow('bag.png');
%       info = imfinfo('bag.png');
%       imageinfo(h,info);
%
%       imshow('trees.tif');
%       imageinfo;
%
%  See also DICOMINFO, IMATTRIBUTES, IMFINFO, IMFORMATS, IMTOOL.

%   Copyright 1993-2004 The MathWorks, Inc.
%   $Revision.1 $  $Date: 2004/08/10 01:49:00 $

if ~isJavaFigure
  eid = sprintf('Images:%s:needJavaFigure',mfilename);
  msg = sprintf('%s is not available on this platform.',upper(mfilename));
  error(eid,'%s',msg);
end

[h,metadata,metadataName] = parseInputs(varargin{:});

infoName = 'Image Info';
hout = createInfoTool;


    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function hout = createInfoTool

        hout = figure('IntegerHandle','off',...
                      'NumberTitle','off',...
                      'Name',infoName,...
                      'Toolbar','none',...
                      'Menubar','none',...
                      'DockControls','off',...  % g206703,g209048
                      'Visible','off');

        % turn on resizing when MATLAB's uitable
        % painting issues are fixed. g206703, g209048
        set(hout,'Resize','off');
        
        % need this menu as a workaround to g221152.
        m = uimenu(hout);
    end

detailPanelExists = false;
metadataPanelExists = false;

% Assign so has scope in nested fcns.
houtPos = get(hout,'Position');
fudge = 4; %works b/c uitable text size cannot be modified.

if isempty(h)

    % create metadata panel
    hMetadataPanel = createMetadataPanel;
    metadataPanelExists = true;

    setFigurePositionBasedOnMetadata;

elseif isempty(metadata)

    % create details panel
    [imageHandle,targetFig] = checkhandle(h);
    hDetailsPanel = createDetailsPanel;
    detailPanelExists = true;

    setFigurePositionBasedOnDetails;

else

    % create details and metadata panels
    [imageHandle,targetFig] = checkhandle(h);
    hDetailsPanel = createDetailsPanel;
    detailPanelExists = true;

    hMetadataPanel = createMetadataPanel;
    metadataPanelExists = true;

    setFigurePositionBasedOnDetailsAndMetadata;

end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function [hPanel,hTablePanel] = createDetailsPanel

        hPanel = uipanel('Parent', hout,...
                         'Units','pixels',...
                         'BorderType','none');

        targetFigName = createFigureName('',targetFig);
        set(hout,'Name',sprintf('%s%s',infoName,targetFigName));

        infoLabelName = sprintf('Image details %s',targetFigName);
        infoLabel = uicontrol('Parent', hPanel,...
                              'Style','text',...
                              'HorizontalAlignment','left',...
                              'Units','pixels',...
                              'String', infoLabelName);
        set(infoLabel,'FontSize',get(infoLabel,'FontSize')+1);

        % Position label in upper left of hDetailsPanel;
        labelExtent = get(infoLabel,'Extent');
        hdPos = get(hPanel,'Position');
        setPanelLabelPosition(infoLabel,hdPos,labelExtent);

        % Create details table
        attributes = imattributes(imageHandle);
        hTablePanel = ipttable(hPanel,attributes);
        set(hTablePanel,'tag','detailsTable');

        %Position hTablePanel below label
        origTablePos = get(hTablePanel,'Position');
        set(hPanel,'ResizeFcn',@resizeLabelAndTable);
        setDetailPanelPos(hPanel);

        tableColor = get(hTablePanel,'BackgroundColor');
        set(hPanel,'BackgroundColor', tableColor);
        set(infoLabel,'BackgroundColor',tableColor);

        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function resizeLabelAndTable(obj,evt)
            hpPos = get(hPanel,'Position');
            infoLabelPos = get(infoLabel,'Position');
            setPanelLabelPosition(infoLabel,hpPos,infoLabelPos);
            set(hTablePanel,'Position',...
                            [1 ...
                             hpPos(4)-infoLabelPos(4)-fudge*2-origTablePos(4) ...
                             origTablePos(3) ...
                             origTablePos(4)]);
        end

    end %createDetailsPanel

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function setDetailPanelPos(detailPanel)
        % Position detailPanel in upper
        % left of hout
        houtPos = get(hout,'Position');
        label = findobj(detailPanel,'type','uicontrol');
        infoLabelPos = get(label,'Position');
        tablePanel = findobj(detailPanel,'tag','detailsTable');
        tablePanelPos = get(tablePanel,'Position');
        set(detailPanel,...
            'Position',...
            [1 ...
            houtPos(4)-infoLabelPos(4)-tablePanelPos(4)-fudge ...
            fudge+max(tablePanelPos(3),infoLabelPos(3)) ...
            fudge+infoLabelPos(4)+tablePanelPos(4)]);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function resizeDetailsPanel(obj,evt,panel)
        setDetailPanelPos(panel);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function hPanel = createMetadataPanel

        hPanel = uipanel('Parent', hout,...
            'Units','pixels',...
            'BorderType','none');

        metadataLabel = uicontrol('Parent', hPanel,...
            'Style','text',...
            'HorizontalAlignment','left',...
            'Units','pixels',...
            'String', sprintf('Metadata %s',metadataName));
        set(metadataLabel,'FontSize',get(metadataLabel,'FontSize')+1);

        % Position label in upper left of hPanel.
        labelExtent = get(metadataLabel,'Extent');
        hpPos = get(hPanel,'Position');
        setPanelLabelPosition(metadataLabel,hpPos,labelExtent);

        % Create metadata table
        hMetadataPanel = ipttable(hPanel, metadata);

        %Position hMetadataPanel below label in hPanel
        setTablePos = @(panelPos,labelPos) ...
            set(hMetadataPanel,...
            'Position',...
            [1 ...
            1 ...
            panelPos(3) ...
            panelPos(4)-labelPos(4)-fudge*2]);
        metadataLabelPos = get(metadataLabel,'Position');
        setTablePos(hpPos,metadataLabelPos);

        tableColor = get(hMetadataPanel,'BackgroundColor');
        set(hPanel,'BackgroundColor',tableColor);
        set(metadataLabel,'BackgroundColor',tableColor);
        set(hPanel,'ResizeFcn',@resizeLabelAndTable);


        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function resizeLabelAndTable(obj,evt)
            hpPos = get(hPanel,'Position');
            metadataLabelPos = get(metadataLabel,'Position');
            setPanelLabelPosition(metadataLabel,...
                hpPos,metadataLabelPos);
            setTablePos(hpPos,metadataLabelPos);

        end
    end %createMetadataPanel

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function setPanelLabelPosition(hLabel,panelPos,oldLabelPos)
        set(hLabel,'Position',[fudge ...
            panelPos(4)-oldLabelPos(4)-fudge ...
            oldLabelPos(3) oldLabelPos(4)]);
    end


    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function resizeMetadataPanel(obj,evt)
        houtPos = get(hout,'Position');
        set(hMetadataPanel,'Position',[0 0 houtPos(3) houtPos(4)]);

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function resizeDetailsAndMetadata(obj,evt)

        oldhoutUnits = get(hout,'Units');
        set(hout,'Units','pixels')
        houtPos = get(hout,'Position');

        oldhbipUnits = get(hDetailsPanel,'Units');
        set(hDetailsPanel,'Units','pixels');

        hdPos = get(hDetailsPanel,'Position');
        set(hDetailsPanel,'Position',[fudge houtPos(4)- hdPos(4) hdPos(3) ...
            hdPos(4)]);

        hdPos = get(hDetailsPanel,'Position');
        oldhmUnits = get(hMetadataPanel,'Units');
        set(hMetadataPanel,'Units','pixels');
        set(hMetadataPanel,'Position',[0 0 houtPos(3) houtPos(4)-hdPos(4)]);

        set(hDetailsPanel,'Units',oldhbipUnits);
        set(hout,'Units',oldhoutUnits);
        set(hMetadataPanel,'Units',oldhmUnits);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function setFigurePositionBasedOnMetadata

        hMetadataPanelPos = get(hMetadataPanel,'Position');
        set(hout,'ResizeFcn',@resizeMetadataPanel);
        set(hout,'Position',[houtPos(1) houtPos(2) hMetadataPanelPos(3)+fudge ...
            hMetadataPanelPos(4)]);

        if ~isempty(metadataName)
            imageinfoName = sprintf('%s %s', infoName, metadataName);
            set(hout,'Name',imageinfoName);
        end
        set(hout,'Color',get(hMetadataPanel,'BackgroundColor'));

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function setFigurePositionBasedOnDetails

        hDetailsPanelPos = get(hDetailsPanel,'Position');
        set(hout,'ResizeFcn',{@resizeDetailsPanel,hDetailsPanel});
        set(hout,'Position',[houtPos(1) houtPos(2) hDetailsPanelPos(3)+fudge ...
            hDetailsPanelPos(4)]);
        set(hout,'Color',get(hDetailsPanel,'BackgroundColor'));
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    function setFigurePositionBasedOnDetailsAndMetadata

        hDetailsPanelPos = get(hDetailsPanel,'Position');
        set(hMetadataPanel,'Position',[0 0 houtPos(3) ...
            houtPos(4)-hDetailsPanelPos(4)]);

        set(hout,'Color',get(hMetadataPanel,'BackgroundColor'));
        set(hout,'ResizeFcn',@resizeDetailsAndMetadata);

    end

if detailPanelExists

  iptwindowalign(targetFig,'left',hout,'left','nodrawnow');
  iptwindowalign(targetFig,'bottom',hout,'top');
  
    if metadataPanelExists
      % delete detailPanel if target image changes
      reactToImageChangesInFig(imageHandle,@deleteFcn);
    else
      % update detailPanel if target image changes
      %imageListener = reactToImageChangesInFig(imageHandle,@deleteFcn,@ ...
      %                                         recreateFcn);
      %set(hout,'DeleteFcn',{@removeListener,imageHandle,imageListener});
      
      % delete detailPanel if target image changes
      reactToImageChangesInFig(imageHandle,@deleteFcn);
    end
end

    %%%%%%%%%%%%%%%%%%
    function deleteFcn

        if ishandle(hDetailsPanel)
            delete(hDetailsPanel);

            if ~metadataPanelExists
                if ishandle(hout)
                    %set(hout,'Visible','off');
                    delete(hout);
                end
            else
                if ~isempty(metadataName)
                    imageinfoName = sprintf('%s %s', infoName, metadataName);
                    set(hout,'Name',imageinfoName);
                else
                    set(hout,'Name',infoName);
                end
                setFigurePositionBasedOnMetadata;
                iptwindowalign(targetFig,'bottom',hout,'top');
            end
        end
    end

    %%%%%%%%%%%%%%%%%%%%
    function recreateFcn

        h = findobj(targetFig,'type','image');
        imageHandle = checkhandle(h);

        if ~metadataPanelExists
            delete(hout);
            hout = createInfoTool;

            hDetailsPanel = createDetailsPanel;
            setFigurePositionBasedOnDetails;
            iptwindowalign(targetFig,'bottom',hout,'top');

            set(hout,'Visible','on');
            set(hout,'HandleVisibility','callback');
            imageListener = reactToImageChangesInFig(imageHandle,@ ...
                                                     deleteFcn,@recreateFcn);
            set(hout,'DeleteFcn',{@removeListener,imageHandle,imageListener});
        else
          reactToImageChangesInFig(imageHandle,@deleteFcn);
        end

    end

set(hout,'Visible','on');
set(hout,'HandleVisibility','callback'); %must happen at end b/c cannot set
%uitable parent directly.
if nargout > 0
    hfigure = hout;
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function removeListener(obj,evt,him,listener)
  
removeImageListener(him,listener);

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [him,hFig] = checkhandle(h)

him = imhandles(h);

if isempty(him)
    eid = sprintf('Images:%s:noImageInFigure',mfilename);
    msg = 'H must contain an image.';
    error(eid,'%s',msg);
elseif ~isscalar(him)
    wid = sprintf('Images:%s:ignoreMultipleImageHandles',mfilename);
    msg = 'IMAGEINFO expected one image in the figure.  Taking first ';
    msg2 = 'image in the image handle array.';
    him = him(1);
    warning(wid,'%s%s',msg,msg2);
end

hFig = ancestor(him,'figure');

end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [h,metadata,metadataLabel] = parseInputs(varargin)

% assign defaults
h = [];
metadata = [];
metadataLabel = [];
metadataOrFile = [];

iptchecknargin(0,2,nargin,mfilename);

switch nargin
    case 0
        h = get(0,'CurrentFigure');
        if isempty(h)
            eid = sprintf('Images:%s:noImageInFigure',mfilename);
            msg = sprintf('%s expects a current figure containing an image.', ...
                upper(mfilename));
            error(eid,'%s',msg);
        end

    case 1
        if isstruct(varargin{1}) || ischar(varargin{1})
            metadataOrFile = varargin{1};
        elseif ishandle(varargin{1})
            h = varargin{1};
            iptcheckhandle(h,{'image','axes','figure'},mfilename,'H',1);
        else
            eid = sprintf('Images:%s:invalidInputArgument',mfilename);
            msg = 'The first input argument must be a valid handle, structure, ';
            msg2 = 'or filename.';
            error(eid,'%s%s',msg,msg2);
        end

    case 2
        h = varargin{1};
        metadataOrFile = varargin{2};

        iptcheckhandle(h,{'image'},mfilename,'HIMAGE',1);
        
        if ~isstruct(metadataOrFile) && ~ischar(metadataOrFile)
            eid = sprintf('Images:%s:invalidStructureOrFilename',mfilename);
            msg = 'The second input argument must be a valid structure ';
            msg2 = 'or filename.';
            error(eid,'%s%s',msg,msg2);
        end
end

if ~isempty(metadataOrFile)
    [metadata,metadataLabel] = getMetadata(metadataOrFile);
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [metadata,label] = getMetadata(structureOrFile)

if isstruct(structureOrFile)
    metadata = structureOrFile;
    % Take metadata of first first frame if coming from multiframe file.
    metadata = metadata(1);

    fnames = fieldnames(metadata);
    idx = strncmpi('Filename',fnames,length('Filename'));
    if any(idx,1)
        field = fnames(idx == 1);
        label = metadata.(field{1});
        [pathstr,n,ext,ver] = fileparts(label);
        label = sprintf('(%s)',[n ext]);
    else
        label = '';
    end

else %file

    label = structureOrFile;
    try
        metadata = imfinfo(structureOrFile);
    catch
        try
            metadata = dicominfo(structureOrFile);
        catch
            eid = sprintf('Images:%s:couldNotReadFile',mfilename);
            msg = sprintf('Could not read this file: ''%s''.', label);
            error(eid,'%s',msg);
        end
    end
    label = sprintf('(%s)',label);
    % Take metadata of first first frame if coming from multiframe file.
    metadata = metadata(1);

end

end
