function [y,ndx] = SortCell(x, dim)
% SortCell    Sort a cell array in ascending order.
%
% Description: SortCell sorts the input cell array according to the
%   dimensions (columns) specified by the user.
%
% Usage: [Y,NDX] = SortCell(X, DIM)
%
% Input:
%	 X: the cell array to be sorted.
%  DIM: (optional) one or more column numbers. Simply an array of one or
%       more column numbers.  The first number is the primary column on
%       which to sort. Extra column numbers may be supplied if secondary
%       sorting is required. The default value is 1, if no dimension
%       array is supplied.
%
% Output:
%     Y: the sorted cell array.
%   NDX: indices such that Y = X(NDX,:).
%
% Example:    Y = SortCell(X, [3 2])
%
% Note that this function has only been tested on mixed cell arrays
% containing character strings and numeric values.

%   Copyright 2007 Jeff Jackson (Ocean Sciences, DFO Canada)
%   Creation Date: Jan. 24, 2007
%   Last Updated:  Jan. 25, 2007
%   2008 DN v1.1:  Added support for numerical datatypes other than char
%                  and double
%   2016 DN v1.2:  Now also outputs index into input to create output.


% Check input arguments
if nargin == 0
    error('no input arguments were supplied.  at least one is expected.');
elseif nargin == 1
    dim = 1;
elseif nargin == 2
    if ~isnumeric(dim)
        error('the second input argument is not numeric.  at least one numeric value is expected.');
    end
else
    error('too many input arguments supplied.  only two are allowed.');
end
if ~iscell(x)
    error('the first input argument is not a cell array.  a cell array is expected.');
end

% prepare output
ndx = [1:size(x,1)].';

% Now find out if the cell array is being sorted on more than one column.
% If it is then use recursion to call the SortCell function again to sort
% the less important columns first. Repeat calls to SortCell until only one
% column is left to be sorted. Then return the sorted cell array to the
% calling function to continue with the higher priority sorting.
ndim = length(dim);
if ndim > 1
    col     = dim(2:end);
    [x,xi]  = SortCell(x, col);
    ndx     = ndx(xi);
end

% Get the dimensions of the input cell array.
nrows   = size(x,1);

% Retrieve the primary dimension (column) to be sorted.
col     = dim(1);

% Place the cells for this column in variable 'b'.
b       = x(:,col);

% Check each cell in cell array 'b' to see if it contains either a
% character string or numeric value.
qchar   = cellfun(@(x)isa(x,'char') , b);
classes = cellfun(@class            , b,'UniformOutput',false);

% Check to see if cell array 'b' contained only character strings.
% If cell array b contains character strings then do nothing because
% no further content handling is required.
if sum(qchar) == nrows
    % Check to see if cell array 'b' contained only numeric values of
    % the same type.
elseif length(unique(classes))==1 && ismember(unique(classes),{'logical','single','double','float','int8','uint8','int16','uint16','int32','uint32','int64','uint64'})
    % If the cells in cell array 'b' contain numeric values retrieve the cell
    % Contents and change 'b' to a numeric array.
    b = [b{:}];
else
    error('This column (%d) is mixed so sorting cannot be completed.',dim(1));
end

% Sort the current array and return the new index.
[~,xi] = sort(b);

% Using the index from the sorted array, update the input cell array and
% return it.
y   = x(xi,:);
ndx = ndx(xi,:);
