% Implementation of Goldstein unwrap algorithm based on location of
% residues and introduction of branchcuts.
% R. M. Goldstein, H. A. Zebker and C. L. Werner, Radio Science 23, 713-720
% (1988).
% Inputs
% fase      Phase in radians, wrapped between (-pi,pi)
% disp      (optional) = 1 to show progress (will slow down code)
%           will also display the branch cuts
% start     (optional) [y,x] position to start unwrapping. Typically faster 
%           at the center of the array
% Outputs
% faserecon Unwrapped phase ( = fase where phase could not be unwrapped)
% shadow    = 1 where phase could not be unwrapped 
% 31 August, 2010 - Acknowledge if used

% Modified 20 Sept 2010 - Find a safe area to unwrap around the first point

%*-----------------------------------------------------------------------*
%|                                                                       |
%|  Except where otherwise noted, this work is licensed under a          |
%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
%|  International (CC BY-NC-SA 4.0) license.                             |
%|                                                                       |
%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
%|                                                                       |
%|       Author: CXS group, PSI                                          |
%*-----------------------------------------------------------------------*
% You may use this code with the following provisions:
%
% If the code is fully or partially redistributed, or rewritten in another
%   computing language this notice should be included in the redistribution.
%
% If this code, or subfunctions or parts of it, is used for research in a 
%   publication or if it is fully or partially rewritten for another 
%   computing language the authors and institution should be acknowledged 
%   in written form in the publication: “Data processing was carried out 
%   using the “cSAXS software package” developed by the CXS group,
%   Paul Scherrer Institut, Switzerland.” 
%   Variations on the latter text can be incorporated upon discussion with 
%   the CXS group if needed to more specifically reflect the use of the package 
%   for the published work.
%
% A publication that focuses on describing features, or parameters, that
%    are already existing in the code should be first discussed with the
%    authors.
%   
% This code and subroutines are part of a continuous development, they 
%    are provided “as they are” without guarantees or liability on part
%    of PSI or the authors. It is the user responsibility to ensure its 
%    proper use and the correctness of the results.   

function [faserecon shadow] = goldsteinunwrap2(fase,disp,start)


display('Unwrapping with Goldstein algorithm')
[nr nc] = size(fase);
if nargin < 2
    disp = 0;
end
if nargin <3
    nrstart = round(nr/2);
    ncstart = round(nc/2);
else
    nrstart = start(1);
    ncstart = start(2);
end

residues =            wrapToPi(fase(2:end,1:end-1)   - fase(1:end-1,1:end-1));
residues = residues + wrapToPi(fase(2:end,2:end)     - fase(2:end,1:end-1));
residues = residues + wrapToPi(fase(1:end-1,2:end)   - fase(2:end,2:end));
residues = residues + wrapToPi(fase(1:end-1,1:end-1) - fase(1:end-1,2:end));
residues = residues/(2*pi);
%%% Find residues
[posr,posc] = find(round(residues)==1);
respos = [posr posc ones(length(posr),1)];
[posr,posc] = find(round(residues)==-1);
resneg = [posr posc -ones(length(posr),1)];
%[posr,posc] = find(round(residues)~=0);
%res = [posr posc];
%res = [respos;resneg];
nres = length(respos(:,1))+length(resneg(:,1));
display(['Found ' num2str(nres) ' residues'])

if nres == 0,
    faserecon = unwrap(unwrap(fase')');
    shadow = faserecon*0;
    return;
end

%%% Find minimum length walls
%currentwall = residues*0;
currentwall = zeros(nr+2,nc+2);
%currentwallcharge = 0;
%wallsegdone = 0;

%currentwall(res(1,1)+1,res(1,2)+1) = 1;


for ii = 1:min(length(respos(:,1)),length(resneg(:,1))),
    dist = (respos(1,1) - resneg(:,1)).^2 + (respos(1,2)-resneg(:,2)).^2;
    ind = find(dist == min(dist),1,'first');
    if sqrt(dist(ind)) < min(nc,nr)/4,%/4
        
        currentwall( respos(1,1)+1,min(respos(1,2),resneg(ind,2))+1 : max(respos(1,2),resneg(ind,2))+1 ) = 1;
        currentwall(min(resneg(ind,1),respos(1,1))+1:max(resneg(ind,1),respos(1,1))+1,resneg(ind,2)+1) = 1;
    
        respos = respos(2:end,:); % Remove from respos
        resaux = resneg(1:ind-1,:);
        resaux = [resaux;resneg(ind+1:end,:)];
        resneg = resaux; 
    else  % Wall too long between them, send to window edge
        % for respos
        distedges = [nr-respos(1,1) respos(1,1) nc-respos(1,2) respos(1,2)]; %upper, lower, right, left
        switch min(distedges)
            case distedges(1) %upper
                currentwall(respos(1,1)+1:nr+2, respos(1,2)+1 ) = 1;
            case distedges(2) %lower
                currentwall(1:respos(1,1)+1, respos(1,2)+1) = 1;  
            case distedges(3); %right
                currentwall(respos(1,1)+1, respos(1,2)+1:nc+2) = 1;
            case distedges(4); %left
                currentwall(respos(1,1)+1, 1:respos(1,2)+1) = 1;
        end
        
        % for resneg
        distedges = [nr-resneg(ind,1) resneg(ind,1) nc-resneg(ind,2) resneg(ind,2)]; %upper, lower, right, left
        switch min(distedges)
            case distedges(1) %upper
                currentwall(resneg(ind,1)+1:nr+2, resneg(ind,2)+1 ) = 1;
            case distedges(2) %lower
                currentwall(1:resneg(ind,1)+1, resneg(ind,2)+1) = 1;  
            case distedges(3); %right
                currentwall(resneg(ind,1)+1, resneg(ind,2)+1:nc+2) = 1;
            case distedges(4); %left
                currentwall(resneg(ind,1)+1, 1:resneg(ind,2)+1) = 1;
        end   
            
    end
end 
% else
%     error('Need to implement for unbalanced charge residues')
% end


% Branch cuts for unpaired residues
res = [respos; resneg];
display([num2str(length(res(:,1))) ' unpaired residues'])
for ii = 1:length(res(:,1)),
    distedges = [nr-res(1,1) res(1,1) nc-res(1,2) res(1,2)]; %upper, lower, right, left
    switch min(distedges)
        case distedges(1) %upper
            currentwall(res(1,1)+1:nr+2, res(1,2)+1 ) = 1;
        case distedges(2) %lower
            currentwall(1:res(1,1)+1, res(1,2)+1) = 1;  
        case distedges(3); %right
            currentwall(res(1,1)+1, res(1,2)+1:nc+2) = 1;
        case distedges(4); %left
            currentwall(res(1,1)+1, 1:res(1,2)+1) = 1;
    end
    res = res(2:end,:);
end
if disp == 1,
figure(4);
imagesc(currentwall);
colorbar
axis xy
title('Branch cuts')
colormap gray
drawnow
end

%% Safe unwrap from start position (this could be made faster)
% Only defined the maximum square, could be made faster by defining a
% rectangle for example
[wallposy wallposx] = find(currentwall == 1); % finds wall positions
%distnearest = (wallposy-nrstart).^2+(wallposx-ncstart).^2;
%distnearest = abs(wallposy-nrstart)+abs(wallposx-ncstart);
distnearest = max(abs(wallposy-nrstart),abs(wallposx-ncstart));
indi = find(distnearest == min(distnearest),1);
longi = min(distnearest)-2;
%longi = min([longi nrstart-1 ncstart-1 nc-ncstart-1 nr-nrstart-1]);

% figure(100); 
% plot(wallposx,wallposy,'o'); 
% hold on, 
% plot(ncstart,nrstart,'or'), 
% plot([-longi longi]+ncstart,[-longi -longi]+nrstart,'-r'); 
% plot([-longi longi]+ncstart,[longi longi]+nrstart,'-r'); 
% plot([-longi -longi]+ncstart,[longi -longi]+nrstart,'-r'); 
% plot([longi longi]+ncstart,[longi -longi]+nrstart,'-r'); 
% hold off,
%%


faserecon = fase*0;
shadow = faserecon+1; % not unwrapped yet

% faserecon(nrstart,ncstart) = fase(nrstart,ncstart);
% shadow(nrstart,ncstart) = 0;
% counter = 0;

% faserecon(nrstart+[-longi:longi],ncstart+[-longi:longi]) ...
%     = unwrap(unwrap(  fase(nrstart+[-longi:longi],ncstart+[-longi:longi])')');
% shadow(nrstart+[-longi:longi],ncstart+[-longi:longi]) = 0;

xmask = [max(1,ncstart-longi):min(nc,ncstart+longi)];
ymask = [max(1,nrstart-longi):min(nr,nrstart+longi)];   
faserecon(ymask,xmask) = unwrap(unwrap(  fase(ymask,xmask)')');
shadow(ymask,xmask) = 0;



counter = 0;


% Start unwrapping
maxiter = 2*max(nr,nc);
wallvert = currentwall(1:end-1,:)&currentwall(2:end,:); % prevents horizontal integration
%wallvert = [zeros(1,nc-1);wallvert;zeros(1,nc-1)];
wallhor = currentwall(:,1:end-1)&currentwall(:,2:end); % prevents horizontal integration
%wallhor = [zeros(nr-1,1) wallhor zeros(nr-1,1)];


while (counter <maxiter)&&(max(shadow(:))==1);
    shadowprev = shadow;
    %%%%% Step right
    %newrec =  [zeros(nr,1) shadow(:,2:end)-shadow(:,1:end-1)] == 1;
    newrec =  [false(nr,1) shadow(:,2:end)&not(shadow(:,1:end-1))];
    %prev = [zeros(nr,1) shadow(:,2:end)-shadow(:,1:end-1)]   == -1;
    % Block forbiden paths here
    newrec(:,2:end) = newrec(:,2:end)&(1-wallvert(1:end-1,2:end-2));
    deltafase = [zeros(nr,1) fase(:,2:end)-faserecon(:,1:end-1)].*newrec;
    %faserecon = faserecon + (fase - round(deltafase/(2*pi))*2*pi).*newrec;
    faserecon(newrec) = fase(newrec) - round(deltafase(newrec)/(2*pi))*2*pi;
    shadow(newrec) = 0;
    
    %%%%% Step left
    %newrec =  [shadow(:,1:end-1)-shadow(:,2:end) zeros(nr,1)] == 1;
    newrec =  [shadow(:,1:end-1)&not(shadow(:,2:end)) false(nr,1)];
    %prev = [zeros(nr,1) shadow(:,2:end)-shadow(:,1:end-1)]   == -1;
    % Block forbiden paths here
    newrec(:,1:end-1) = newrec(:,1:end-1)&(1-wallvert(1:end-1,2:end-2));
    deltafase = [fase(:,1:end-1)-faserecon(:,2:end) zeros(nr,1)].*newrec;
    %faserecon = faserecon + (fase - round(deltafase/(2*pi))*2*pi).*newrec;
    faserecon(newrec) = fase(newrec) - round(deltafase(newrec)/(2*pi))*2*pi;
    shadow(newrec) = 0;
        
    %%%%% Step up (positive y)
    %newrec =  [zeros(1,nc) ; shadow(2:end,:)-shadow(1:end-1,:)] == 1;
    newrec =  [false(1,nc) ; shadow(2:end,:)&not(shadow(1:end-1,:))];
    %prev = [zeros(nr,1) shadow(:,2:end)-shadow(:,1:end-1)]   == -1;
    % Block forbiden paths here
    newrec(2:end,:) = newrec(2:end,:)&(1-wallhor(2:end-2,1:end-1));
    deltafase = [zeros(1,nc) ; fase(2:end,:)-faserecon(1:end-1,:)].*newrec;
    %faserecon = faserecon + (fase - round(deltafase/(2*pi))*2*pi).*newrec;
    faserecon(newrec) = fase(newrec) - round(deltafase(newrec)/(2*pi))*2*pi;
    shadow(newrec) = 0;
    
    %%%%% Step down (negative y)
    %newrec =  [shadow(1:end-1,:)-shadow(2:end,:) ; zeros(1,nc)] == 1;
    newrec =  [shadow(1:end-1,:)&not(shadow(2:end,:)) ; false(1,nc)];% Logical input does not seeem to help with computing time
    %prev = [zeros(nr,1) shadow(:,2:end)-shadow(:,1:end-1)]   == -1;
    % Block forbiden paths here
    newrec(1:end-1,:) = newrec(1:end-1,:)&(1-wallhor(2:end-2,1:end-1));
    deltafase = [fase(1:end-1,:)-faserecon(2:end,:) ; zeros(1,nc)].*newrec;
    %faserecon = faserecon + (fase - round(deltafase/(2*pi))*2*pi).*newrec;
    faserecon(newrec) = fase(newrec) - round(deltafase(newrec)/(2*pi))*2*pi;
    shadow(newrec) = 0;
    
    counter = counter+1;
    
    if any(not(shadow(:)==shadowprev(:))) == 0,
        warning('Not all points are accessible for integration')
        faserecon(shadow==1) = fase(shadow==1);
        break;
    end
    
    
if disp == 1,
    figure(5);
    imagesc(faserecon);
    colorbar
    axis xy
    title('Reconstructed phase')
    colormap jet
    drawnow;
end
    
end

if counter == maxiter,
    warning('Maximum number of iterations exceeded for unwrapping. Increase maxiter.'),
end