%  IMSHIFT_FAST shift of stack of images by given number of
%  pixels and if needed crop / pad image to fit into Npix_new
%
%  img_new=imshift_fast(img_0, x,y, Npix_new=[], type='linear', default_val=0)
%
% Inputs:
%   **img_0       - stack of images 
%   **x,y         - horizontal / vertical shift in pixels  (scalars)
%   **Npix_new    - empty/missing => keep original size, 2x1 vector => embed new image into given frame size 
%   **type        - linear / nearest neighbor interpolation , (missing/empty => linear)
%   **default_val - default value to fill empty regions created after the image shift 
% returns: 
%   ++ img_new    - shifted image padded to Npix_new


%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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 img_new=imshift_fast(img_0, x,y, Npix_new, type, default_val)

    
    if nargin < 4 || isempty(Npix_new)
        Npix_new = size(img_0);
    end
    Npix_new = Npix_new(1:2);
    if nargin < 5
        type = 'linear';
    end
    if nargin < 6
        default_val = 0;
    end
    if length(x) > 1 || length(y) > 1
        error('Only scalar position shifts are accepted')
    end
    
    [Nx, Ny, Nimgs] = size(img_0);


    if x==0 && y == 0 && all([Nx,Ny] == Npix_new)
        %% no change is needed, return original image
        img_new = img_0;
        return
    end
    

    pos = -[x,y];
    if strcmp(type, 'linear') && any(round([x,y]) ~= [x,y]) &&  ~isa(img_0, 'logical')
        shift = make_shift(pos - round(pos));
        
        Npix_tmp = size(img_0);
        Npix_tmp(1:2) = Npix_tmp(1:2) + 2;
        img_tmp = zeros(Npix_tmp,  'like', img_0 );
        for i = 1:Nimgs
            img_tmp(:,:,i) = conv2(img_0(:,:,i), shift, 'full');
        end
        img_0 = img_tmp;
    end
    Npix = [Nx,Ny];

    [oROI, pROI] = find_ROI( pos ,Npix_new, Npix );
    

    if all(x==0) && all(y == 0) && all(Npix_new < Npix) 
        img_new = img_0(pROI{:},:);
    else
        if all(abs(pos) <= 1) && all( Npix_new == Npix)
            img_new = img_0;  % for tiny shift reuse the original array 
        else
            img_new = ones([Npix_new,Nimgs], 'like', img_0 )*default_val;
        end
        img_new(oROI{:}, :) = img_0(pROI{:}, :);
    end
end

function [oROI, pROI, oROI_, pROI_] = find_ROI( position, Nobj_new, Nobj_0 )

    oROI = cell(2,1);
    pROI = cell(2,1);
    
    pos = round(position([2,1]));
    %% correction for odd size of the Nobj_new 
    pos = pos - mod(Nobj_0-Nobj_new,2) .* (Nobj_new > Nobj_0); 
    oROI_ = zeros(2);
    pROI_ = zeros(2);
    for dim = 1:2
        range_0 = round(pos(dim) + [1,Nobj_0(dim)] - Nobj_0(dim)/2 + Nobj_new(dim)/2);
        oROI_(dim,:) = min(max(1,range_0), Nobj_new(dim));
        l = oROI_(dim,2) - oROI_(dim,1) +1;
        p1 = min(Nobj_0(dim), Nobj_0(dim) - (range_0(2) - Nobj_new(dim)));
        pROI_(dim,1) = p1 - l+1;
        pROI_(dim,2) = p1;
        if pROI_(dim,1) < pROI_(dim,2)
            pROI{dim} = pROI_(dim,1):pROI_(dim,2);
        else
            pROI{dim} = [];
        end
        if oROI_(dim,1) < oROI_(dim,2)
            oROI{dim} = oROI_(dim,1):oROI_(dim,2);
        else
            oROI{dim} = [];
        end
    end
end


function shift_mat  = make_shift(shift)
    
    x = shift(2);  % correction on pixel position 
    y = shift(1);
    N =  max(1, ceil(abs([x,y])));
    x = x+N(1);
    y = y+N(2);
    dx = x-floor(x);
    dy = y-floor(y);
    
        
    w(1) = dx * dy;
    w(2) = (1-dx) * dy;
    w(3) = dx * (1-dy);
    w(4) = (1-dx) * (1-dy);
 
    ix = 1+floor(x);
    iy = 1+floor(y);

    shift_mat = zeros(2*N+1);
    shift_mat(ix, iy) = w(4);
    shift_mat(ix+1, iy) = w(3);
    shift_mat(ix, iy+1) = w(2);
    shift_mat(ix+1, iy+1) = w(1);    
end
