function fout = waitbar2(x, whichbar, varargin)
% WAITBAR2 - Displays wait bar with fancy color shift effect
%
% Adaptation of the MatLab standard waitbar function:
%
%   H = WAITBAR2(X,'title', property, value, property, value, ...)
%   creates and displays a waitbar of fractional length X.  The
%   handle to the waitbar figure is returned in H.
%   X should be between 0 and 1.  Optional arguments property and
%   value allow to set corresponding waitbar figure properties.
%   Property can also be an action keyword 'CreateCancelBtn', in
%   which case a cancel button will be added to the figure, and
%   the passed value string will be executed upon clicking on the
%   cancel button or the close figure button.
%
%   WAITBAR2(X) will set the length of the bar in the most recently
%   created waitbar window to the fractional length X.
%
%   WAITBAR2(X, H) will set the length of the bar in waitbar H
%   to the fractional length X.
%
%   WAITBAR2(X, H, 'updated title') will update the title text in
%   the waitbar figure, in addition to setting the fractional
%   length to X.
%
%   WAITBAR2 is typically used inside a FOR loop that performs a
%   lengthy computation.  A sample usage is shown below:
%
%       h = waitbar(0,'Please wait...', 'BarColor', 'g');
%       for i = 1:100,
%           % computation here %
%           waitbar(i/100, h);
%       end
%       close(h);
%
% Examples for the 'BarColor' option:
%   - Standard color names: 'red', 'blue', 'green', etcetera
%   - Standard color codes: 'r', 'b', 'k', etcetera
%   - A RGB vector, such as [.5 0 .5] (deep purple)
%   - Two RGB colors in a matrix, such as [1 0 0; 0 0 1] (gradient red-blue)
%
% The latter example shows how to create a custom color-shift effect. The top
% row of the 2x3 matrix gives the initial color, and the bottom row the
% end color.
%
%   Clay M. Thompson 11-9-92
%   Vlad Kolesnikov  06-7-99
%   Jasper Menger    12-5-05 ['BarColor' option added, code cleaned up]
%   (Copyright 1984-2002 The MathWorks, Inc.)

if nargin >= 2
    if ischar(whichbar)
        % We are initializing
        type = 2; 
        name = whichbar;
    elseif isnumeric(whichbar)
        % We are updating, given a handle
        type = 1; 
        f    = whichbar;
    else
        error(['Input arguments of type ', class(whichbar), ' not valid.']);
    end
elseif nargin == 1
    f = findobj(allchild(0), 'flat', 'Tag', 'TMWWaitbar');
    if isempty(f)
        type = 2;
        name = 'Waitbar';
    else
        type = 1;
        f    = f(1);
    end
else
    error('Input arguments not valid.');
end

% Progress coordinate (0 - 100%)
x = max(0, min(100 * x, 100));

switch type
    case 1
        % waitbar(x) update
        p = findobj(f, 'Tag', 'patch');
        l = findobj(f, 'Tag', 'line');
        if isempty(f) | isempty(p) | isempty(l),
            error('Couldn''t find waitbar handles.');
        end
        xpatch  = [0 x x 0];
        xline   = get(l, 'XData');
        b_map   = get(f, 'ColorMap');
        p_color = b_map(floor(x / 100 * 63) + 1, :);
        set(p, 'XData'    , xpatch);
        set(p, 'FaceColor', p_color);
        set(l, 'XData'    , xline);

        if nargin > 2
            % Update waitbar title
            hAxes  = findobj(f, 'type', 'axes');
            hTitle = get(hAxes, 'title');
            set(hTitle, 'string', varargin{1});
        end

    case 2
        % waitbar(x,name) initialize
        vertMargin = 0;
        if nargin > 2
            % we have optional arguments: property-value pairs
            if rem(nargin, 2) ~= 0
                error( 'Optional initialization arguments must be passed in pairs' );
            end
        end

        % Set units to points, and put the waitbar in the centre of the screen
        oldRootUnits = get(0,'Units');
        set(0, 'Units', 'points');
        
        screenSize     = get(0,'ScreenSize');
        axFontSize     = get(0,'FactoryAxesFontSize');
        pointsPerPixel = 72/get(0,'ScreenPixelsPerInch');
        width          = 360 * pointsPerPixel;
        height         = 75 * pointsPerPixel;
        pos            = [screenSize(3) / 2 - width / 2, ...
                          screenSize(4) / 2 - height / 2, ...
                          width, height];

        % Default color shift: dark red -> light
        barcolor1 = [0.5 0 0];
        barcolor2 = [1.0 0 0];

        f = figure(...
            'Units'        , 'points', ...
            'BusyAction'   , 'queue', ...
            'Position'     , pos, ...
            'Resize'       , 'off', ...
            'CreateFcn'    , '', ...
            'NumberTitle'  , 'off', ...
            'IntegerHandle', 'off', ...
            'MenuBar'      , 'none', ...
            'Tag'          , 'TMWWaitbar',...
            'Interruptible', 'off', ...
            'Visible'      , 'off');

        %%%%%%%%%%%%%%%%%%%%%
        % set figure properties as passed to the function
        % pay special attention to the 'cancel' request
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        
        if nargin > 2
            propList         = varargin(1:2:end);
            valueList        = varargin(2:2:end);
            cancelBtnCreated = 0;
            for ii = 1:length(propList)
                try
                    if strcmp(lower(propList{ii}), 'createcancelbtn') & ~cancelBtnCreated
                        % Create a cancel button
                        cancelBtnHeight = 23 * pointsPerPixel;
                        cancelBtnWidth  = 60 * pointsPerPixel;
                        newPos          = pos;
                        vertMargin      = vertMargin + cancelBtnHeight;
                        newPos(4)       = newPos(4) + vertMargin;
                        callbackFcn     = [valueList{ii}];
                        set(f, 'Position', newPos, 'CloseRequestFcn', callbackFcn);
                        cancelButt = uicontrol(...
                            'Parent'       , f, ...
                            'Units'        , 'points', ...
                            'Callback'     , callbackFcn, ...
                            'ButtonDownFcn', callbackFcn, ...
                            'Enable'       , 'on', ...
                            'Interruptible', 'off', ...
                            'String'       , 'Cancel', ...
                            'Tag'          , 'TMWWaitbarCancelButton', ...
                            'Position'     , [pos(3) - cancelBtnWidth * 1.4, 7, ...
                                              cancelBtnWidth, cancelBtnHeight]);                        
                        cancelBtnCreated = 1;
                    elseif strcmp(lower(propList{ii}), 'barcolor')
                        % Set color of waitbar
                        barcolor = valueList{ii};
                        if ischar(barcolor)
                            % Character color input: convert color code or name to RGB vector
                            switch lower(barcolor)
                                case {'r', 'red'}    , barcolor2 = [1 0 0];
                                case {'g', 'green'}  , barcolor2 = [0 1 0];
                                case {'b', 'blue'}   , barcolor2 = [0 0 1];
                                case {'c', 'cyan'}   , barcolor2 = [0 1 1];
                                case {'m', 'magenta'}, barcolor2 = [1 0 1];
                                case {'y', 'yellow'} , barcolor2 = [1 1 0];
                                case {'k', 'black'}  , barcolor2 = [0 0 0];
                                case {'w', 'white'}  , barcolor2 = [1 1 1];
                                otherwise            , barcolor2 = rand(1, 3);
                            end
                            % Color shift: dark -> light
                            barcolor1 = 0.5 * barcolor2;
                        else
                            % RGB vector color input
                            barcolor1 = barcolor(1, :);
                            if size(barcolor, 1) > 1
                                barcolor2 = barcolor(2, :);
                            else
                                barcolor2 = barcolor1;
                            end
                        end % of BarColor option
                    else
                        % simply set the prop/value pair of the figure
                        set(f, propList{ii}, valueList{ii});
                    end
                catch
                    % Something went wrong, so display warning
                    disp(sprintf('Warning: could not set property ''%s'' with value ''%s'' ', propList{ii}, num2str(valueList{ii})));
                end % of try
            end % of proplist loop
        end % of setting figure properties

        % -----------------------------------------------------------------
       
        % Create axes
        axNorm = [.05 .3 .9 .2];
        axPos  = axNorm .* [pos(3:4), pos(3:4)] + [0 vertMargin 0 0];
        h = axes(...
            'XLim'          , [0 100], ...
            'YLim'          , [0 1], ...
            'Box'           , 'on', ...
            'Units'         , 'Points', ...
            'FontSize'      , axFontSize, ...
            'Position'      , axPos, ...
            'XTickMode'     , 'manual', ...
            'YTickMode'     , 'manual', ...
            'XTick'         , [], ...
            'YTick'         , [], ...
            'XTickLabelMode', 'manual', ...
            'XTickLabel'    , [], ...
            'YTickLabelMode', 'manual', ...
            'YTickLabel'    , []);

        % Display text on top of axes
        tHandle       = title(name);
        tHandle       = get(h,'title');
        oldTitleUnits = get(tHandle,'Units');
        tExtent       = get(tHandle,'Extent');
        set(tHandle, 'Units', 'points', 'String', name, 'Units', oldTitleUnits);

        % Make sure the lay-out is OK
        titleHeight = tExtent(4) + axPos(2) + axPos(4) + 5;
        if titleHeight > pos(4)
            pos(4)      = titleHeight;
            pos(2)      = screenSize(4) / 2 - pos(4) / 2;
            figPosDirty = true;
        else
            figPosDirty = false;
        end
        if tExtent(3) > pos(3) * 1.1;
            pos(3)       = min(tExtent(3) * 1.1, screenSize(3));
            pos(1)       = screenSize(3) / 2 - pos(3) / 2;
            axPos([1,3]) = axNorm([1, 3]) * pos(3);
            figPosDirty  = true;
            set(h, 'Position', axPos);            
        end
        if figPosDirty
            set(f, 'Position', pos);
        end

        % Create two color gradient colormap
        for i = 1:64
            b_map(i, 1:3) = barcolor1 + (i - 1) / (64 - 1) * (barcolor2 - barcolor1);
        end
        set(f, 'ColorMap', b_map);

        % Draw the bar
        xpatch  = [0 x x 0];
        ypatch  = [0 0 1 1];
        xline   = [100 0 0 100];
        yline   = [0 0 1 1];
        p_color = b_map(floor(x / 100 * 63) + 1, :);
        l_color = get(gca, 'XColor');
        p       = patch(xpatch, ypatch, p_color, 'Tag', 'patch', 'EdgeColor', 'none', 'EraseMode', 'none');
        l       = patch(xline, yline, 'w', 'Tag', 'line', 'FaceColor', 'none', 'EdgeColor', l_color, 'EraseMode', 'none');
        
        % Make figure visible, and restore the original units
        set(f, 'HandleVisibility', 'Callback', 'Visible', 'on');
        set(0, 'Units', oldRootUnits);

end % of case
drawnow;

% Pass on figure handles to output
if nargout == 1,
    fout = f;
end