function fileList = getAllFilesUnderPackages(varargin)
% GETALLFILESUNDERPACKAGES Gets all the MATLAB files under the input packages.
% Returns a cell array of MATLAB files.

% Copyright 2022 The MathWorks, Inc.

    fileList = {};
    pkgs = {};      % unique package names that have already been processed
    if nargin < 1
        error(message('MATLAB:narginchk:notEnoughInputs'));
    end

    % Only process folders
    for i = 1:nargin
        if exist(varargin{i}, 'dir') == 7
            name = getNameFromPath(varargin{i});
            packageObject = meta.package.fromName(name);
            [fileList,pkgs] = appendFileFromPackage(fileList, packageObject, pkgs);
        end
    end
end

% get all MATLAB files directly under a package folder
function [fileList, pkgs] = appendFileFromPackage(fileList, packageObject, pkgs)
    % packageObject could be empty if the package has no MATLAB file/subpackage or it is not a package folder
    if ~isempty(packageObject)
        if isempty(pkgs) || ~any(contains(pkgs,packageObject.Name))
            pkgs = [pkgs, packageObject.Name];
            % get all class files for the package
            if ~isempty(packageObject.ClassList)
                for i = 1 : numel(packageObject.ClassList)
                    fileList = [fileList, {which(packageObject.ClassList(i).Name)}];
                end
            end
            % get all function files for the package
            if ~isempty(packageObject.FunctionList)
                for i = 1 : numel(packageObject.FunctionList)
                    fileList = [fileList, {which([packageObject.Name '.' packageObject.FunctionList(i).Name])}];
                end
            end
    
            % recursive if there is any sub-package
            if ~isempty(packageObject.PackageList)
                for i = 1 : numel(packageObject.PackageList)
                    [fileList, pkgs] = appendFileFromPackage(fileList, packageObject.PackageList(i), pkgs);
                end
            end
        end
    end
end

% Get the fully-qualified package name.
function name = getNameFromPath(sourcePath)
    FQName='';
    fname = '';
    name = '';
    parts = split(sourcePath, '+');
    num_elements = numel(parts);
    packageName = '';

    % If sourcePath is a package, num_elements must be larger than one, construct
    % construct the fully qualified package name starting from the end backward.
    % If sourcePath is not a package, return empty without warning/error since
    % this only happens when the function is called in interactive use, which
    % is not what the function is designed for.
    if num_elements > 1
        packageName = parts{num_elements};

        % Walk backwards through parts until a part is found with a filesep in the middle.
        for i = 1:num_elements - 2
            % Removing the trailing filesep
            part = parts{num_elements - i}(1:end-1);
            if ~contains(part, filesep)
                packageName = [part '.' packageName];
            else
                break;
            end
        end
        if ~isempty(fname)
            FQName = [packageName '.' fname];
        else
            FQName = packageName;
        end
        if FQName(end) == filesep
            FQName = FQName(1:end-1);
        end
    end
    name = FQName;
end