function y = base64decode(x, outfname, alg)
%BASE64DECODE Perform base64 decoding on a string.
%
% INPUT:
%   x    - block of data to be decoded.  Can be a string or a numeric  
%          vector containing integers in the range 0-255. Any character
%          not part of the 65-character base64 subset set is silently
%          ignored.  Characters occuring after a '=' padding character are 
%          never decoded. If the length of the string to decode (after 
%          ignoring non-base64 chars) is not a multiple of 4, then a 
%          warning is generated.
%
%   outfname - if provided the binary date from decoded string will be
%          saved into a file. Since Base64 coding is often used to embbed
%          binary data in xml files, this option can be used to extract and
%          save them.
%
%   alg  - Algorithm to use: can take values 'java' or 'matlab'. Optional
%          variable defaulting to 'java' which is a little faster. If 
%          'java' is chosen than core of the code is performed by a call to
%          a java library. Optionally all operations can be performed using
%          matleb code. 
%
% OUTPUT:
%   y    - array of binary data returned as uint8 
%
%   This function is used to decode strings from the Base64 encoding specified
%   in RFC 2045 - MIME (Multipurpose Internet Mail Extensions).  The Base64
%   encoding is designed to represent arbitrary sequences of octets in a form
%   that need not be humanly readable.  A 65-character subset ([A-Za-z0-9+/=])
%   of US-ASCII is used, enabling 6 bits to be represented per printable
%   character.
%
%   See also BASE64ENCODE.
%
%   Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com
%
%   Matlab version based on 2004 code by Peter J. Acklam
%   E-mail:      pjacklam@online.no
%   URL:         http://home.online.no/~pjacklam
%   http://home.online.no/~pjacklam/matlab/software/util/datautil/base64encode.m

if nargin<3, alg='java';  end
if nargin<2, outfname=''; end

%% if x happen to be a filename than read the file
if (numel(x)<256)
  if (exist(x, 'file')==2)
    fid = fopen(x,'rb');
    x = fread(fid, 'uint8');   
    fclose(fid);
  end
end
x = uint8(x(:)); % unify format

%% Perform conversion
switch (alg)
  case 'java' 
    base64 = org.apache.commons.codec.binary.Base64;
    y = base64.decode(x);
    y = mod(int16(y),256); % convert from int8 to uint8
  case 'matlab'
    %%  Perform the mapping
    %   A-Z  ->  0  - 25
    %   a-z  ->  26 - 51
    %   0-9  ->  52 - 61
    %   + -  ->  62       '-' is URL_SAFE alternative
    %   / _  ->  63       '_' is URL_SAFE alternative
    map = uint8(zeros(1,256)+65);
    map(uint8(['A':'Z', 'a':'z', '0':'9', '+/=']))= 0:64;
    map(uint8('-_'))= 62:63;  % URL_SAFE alternatives
    x = map(x);  % mapping
    
    x(x>64)=[]; % remove non-base64 chars
    if rem(numel(x), 4)
      warning('Length of base64 data not a multiple of 4; padding input.');
    end
    x(x==64)=[]; % remove padding characters
    
    %% add padding and reshape
    nebytes = length(x);         % number of encoded bytes
    nchunks = ceil(nebytes/4);   % number of chunks/groups
    if rem(nebytes, 4)>0
      x(end+1 : 4*nchunks) = 0;  % add padding
    end
    x = reshape(uint8(x), 4, nchunks);
    y = repmat(uint8(0), 3, nchunks);            % for the decoded data
    
    %% Rearrange every 4 bytes into 3 bytes
    %    00aaaaaa 00bbbbbb 00cccccc 00dddddd
    % to form
    %    aaaaaabb bbbbcccc ccdddddd
    y(1,:) = bitshift(x(1,:), 2);                 % 6 highest bits of y(1,:)
    y(1,:) = bitor(y(1,:), bitshift(x(2,:), -4)); % 2 lowest  bits of y(1,:)
    y(2,:) = bitshift(x(2,:), 4);                 % 4 highest bits of y(2,:)
    y(2,:) = bitor(y(2,:), bitshift(x(3,:), -2)); % 4 lowest  bits of y(2,:)
    y(3,:) = bitshift(x(3,:), 6);                 % 2 highest bits of y(3,:)
    y(3,:) = bitor(y(3,:), x(4,:));               % 6 lowest  bits of y(3,:)
    
    %% remove extra padding
    switch rem(nebytes, 4)
      case 2
        y = y(1:end-2);
      case 3
        y = y(1:end-1);
    end
end

%% reshape to a row vector and make it a character array
y = uint8(reshape(y, 1, numel(y)));

%% save to file if needed
if ~isempty(outfname)
  fid = fopen(outfname,'wb');
  fwrite(fid, y, 'uint8');  
  fclose(fid);
end
