function DEM = fillsinks(DEM,maxdepth)

%FILLSINKS fill/remove pits, sinks or topographic depressions
%
% Syntax
%
%     DEMfs = fillsinks(DEM)
%     DEMfs = fillsinks(DEM,maxdepth)
%     DEMfs = fillsinks(DEM,sinks)
%
% Description
%
%     fillsinks removes topographic depressions in a Digital Elevation 
%     Model (DEM). Use this function to enable a continuous flow towards 
%     the DEM edges. 
%
%     Sinks may, however, be closed basins or dolines and as such they are 
%     important features of DEMs. In order to account for such sinks, 
%     fillsinks allows you to specify a maximum depth of sinks, that 
%     will be filled, or to employ a logical grid (sinks) that is true
%     where sinks should remain (minima imposition). Note that for latter
%     option, there will be one regional minima for each connected
%     component in the sinks grid. 
%
% Input
%
%     DEM       digital elevation model (GRIDobj)
%     maxdepth  positive scalar with maximum depth of sinks that will be
%               filled
%     SINKS     logical matrix same size as dem with true elements
%               referring to sinks (GRIDobj)
%
% Output
%
%     DEMfs    digital elevation model with filled sinks (GRIDobj)
%
% Example
%
%     DEM = GRIDobj('srtm_bigtujunga30m_utm11.tif');
%     DEMf = fillsinks(DEM);
%     % display sink depth
%     DIFFDEM = DEMf-DEM;
%     DIFFDEM.Z(DIFFDEM.Z==0) = nan;
%     imageschs(DEM,DIFFDEM.Z);
%
%
% See also: IMFILL, IMRECONSTRUCT, IMIMPOSEMIN, PREPROCESSTOOL
%
% Author: Wolfgang Schwanghart (w.schwanghart[at]geo.uni-potsdam.de)
% Date: 28. January, 2013


% Check input arguments
narginchk(1, 2)

if nargin == 1;
    
else
    if isscalar(maxdepth) && ~isa(maxdepth,'GRIDobj');
        validateattributes(maxdepth,{'numeric'},{'>',0});
        md = true;
    else
        SINKS = maxdepth;
        validatealignment(DEM,SINKS);
        if isa(SINKS,'GRIDobj');           
            SINKS = SINKS.Z;
        end
        md = false;
    end
        
end

dem = DEM.Z;

% START here
% identify nans
Inan      = isnan(dem);
% set nans to -inf
dem(Inan) = -inf;

if nargin == 1;
    % fill depressions using imfill with an 8-neighborhood
    demfs      = imfill(dem,8,'holes');
    
elseif nargin==2 && md;
    
    % create mask
    % complement image
    dem  = imcomplement(dem);
    
    % create marker
    marker = dem;
    marker(2:end-1,2:end-1) = -inf;
    if any(Inan);
        I = (imdilate(Inan,ones(3)) & ~Inan);
        marker(I) = dem(I); 
    end
    
    demfs = imreconstruct(marker,dem);
    
    % difference image between filled and original DEM
    D   = dem-demfs;
    Inan = ~Inan;
    Inan = Inan(:);
    
    while any(D(Inan) > maxdepth)
        
        % find maximum difference in each connected sink area
        I = D>0;
        STATS = regionprops(I,D,'MaxIntensity','PixelIdxList');
        
        for r = 1:numel(STATS);
            if STATS(r).MaxIntensity < maxdepth;
                % do nothing
            else
                [~,ix] = max(D(STATS(r).PixelIdxList));
                ix     = ix(1);
                marker(STATS(r).PixelIdxList(ix)) = dem(STATS(r).PixelIdxList(ix));
            end
        end
        demfs = imreconstruct(marker,dem);
        D   = dem-demfs;
    end
    
    % complement image again
    demfs = imcomplement(demfs);
    Inan  = reshape(~Inan,DEM.size);
else
    % minima imposition
    I = true(size(dem));
    I(2:end-1,2:end-1) = false;
    if any(Inan(:));
        I = (imdilate(Inan,ones(3)) & ~Inan) | I;
    end
    
    % Refine markers by identifying the lowest value in each sink
    STATS = regionprops(SINKS,dem,'MinIntensity','PixelIdxList');
    SINKS = false(size(SINKS));
    for r = 1:numel(STATS);
        ix = find(dem(STATS(r).PixelIdxList)==STATS(r).MinIntensity,1,'first');
        SINKS(STATS(r).PixelIdxList(ix)) = true;
    end
    
    marker = -inf(size(dem),class(dem));
    marker(SINKS) = -dem(SINKS);
    marker(I)     = -dem(I);
    demfs = -imreconstruct(marker,-dem);
    
end

% nans in the dem are set to nan again
try
    demfs(Inan) = nan;
end
DEM.Z = demfs;
