function githubSync(dependencies, varargin)
% This function takes a cell array of url's to hithub repositories, loop through
% them and ensure they exist on the path, or clone them to your local machine.
%
% Example input:
%
% dependencies={...
% 	'https://github.com/drbenvincent/mcmc-utils-matlab',...
% 	'https://github.com/altmany/export_fig'};
%
% Written by Ben Vincent
% https://github.com/drbenvincent/github-sync-matlab

%% Process inputs
p = inputParser;
p.FunctionName = mfilename;
p.addRequired('dependencies',@iscellstr);
p.addParameter('exclude',[], @isvector);
p.addParameter('selfUpdate',[], @islogical);
p.parse(dependencies, varargin{:});

% deal with row or column inputs
if iscolumn(p.Results.dependencies)
	dependencies = p.Results.dependencies'; 
end

% Return to original path on cleanup
originalPath = cd;
returnToOrginalDir = onCleanup(@() cd(originalPath));

% Optionally exclude dependencies
if ~isempty(p.Results.exclude)
	assert(numel(dependencies)==numel(p.Results.exclude),...
		'exclude and dependencies must be same length')
	dependencies = dependencies(p.Results.exclude ~= true);
	if isempty(dependencies)
		return
	end
end

% Shall we update the updater? Recursive black-magic
if p.Results.selfUpdate
	githubSync({'https://github.com/drbenvincent/github-sync-matlab'})
end

%% Main algorithm
for url=dependencies
	cloneOrUpdateDependency(url{:});
end

end


function cloneOrUpdateDependency(url)
displayDependencyToCommandWindow(url);
repoName = getRepoNameFromUrl(url);
if ~isRepoFolderOnPath(repoName)
	addpath(fullfile(defineInstallPath(),repoName)); % TODO: check this... on first install, the folder won't exist
	cloneGitHubRepo(url, defineInstallPath());
else
	updateGitHubRepo(findPathContainingRepo(repoName),repoName);
end
end

function displayDependencyToCommandWindow(url)
disp( makeHyperlink(url, makeWeblinkCode(url)) )
end

function repoName = getRepoNameFromUrl(url)
[~,repoName] = fileparts(url);
end

function installPath = defineInstallPath()
% installPath will be the Matlab userpath (eg /Users/Username/Documents/MATLAB)
if isempty(userpath)
	userpath('reset')
end
installPath = userpath;
% Fix the trailing ":" which only sometimes appears
installPath = removeTrailingColon(installPath);
end

function str = removeTrailingColon(str)
if str(end)==':'
	str(end)='';
end
end

function onPath = isRepoFolderOnPath(repoName)
	onPath = exist(repoName,'dir')==7;
end

function cloneGitHubRepo(repoAddress, installPath)
	try
		cd(installPath)
		command = sprintf('git clone %s.git', repoAddress);
		system(command);
	catch
		error('git clone failed')
	end
end

function updateGitHubRepo(installPath,repoName)
try
	% TODO: confirm this works when the repo is private.
	cd(fullfile(installPath,repoName))
	system('git pull');
catch
	warning('Unable to update GitHub repository')
end
end

function repoPath = findPathContainingRepo(repoName)
% requested repoName was found as a folder on the path. This function
% returns the path of the folder containing that repo.
allPaths = strsplit(path, systemDelimiter());
isMatch = cell2mat(cellfun(@isTargetPath, allPaths, 'UniformOutput', false));
if sum(isMatch)==1
		repoPath = fileparts(allPaths{ find(isMatch,1) });
		return
elseif sum(isMatch)==0
	error('previous found repo on path, but failed to locate path')
elseif sum(isMatch)>1
	disp(allPaths{isMatch==1})
	error('found multiple possible folders that could correspond to target repo')
end

	function isTarget = isTargetPath(p)
		isTarget = strcmp(repoName, getFolderFromPath(p));
	end
	function foldername = getFolderFromPath(p)
		[~, foldername] = fileparts(p);
	end
end

function delimiter = systemDelimiter()
if ismac
		delimiter = ':';
	elseif ispc
		delimiter = ';';
end
end