classdef MatlabPathManager
    properties
        projectFileName = '.subpaths';
        stateFileName = 'state';
    end

    methods
        function obj = MatlabPathManager()
        end

        function p = installLocation(obj)
            nameCurrentFile = mfilename('fullpath');
            pathParts = split(nameCurrentFile, filesep);

            p = join(pathParts(1:end-2), filesep);
            p = p{1};
        end

        function p = stateFilePath(obj)
            p = fullfile(obj.installLocation(), obj.stateFileName);
        end

        % Load a given subpath into the state file and the matlab path and do savepath(), atomically
        function loadSubpath(obj, p)
            state = mpm.PersistentState(obj.stateFilePath);

            addpath(p);
            state.subpaths(p) = true;
            state.saveState();

            savepath() % after save state to make sure saved paths are always present in the state
        end

        % Unload a given subpath from the state file and the matlab path and do savepath(), atomically
        function unloadSubpath(obj, p)
            state = mpm.PersistentState(obj.stateFilePath);

            rmpath(p);
            savepath() % before save state to make sure saved paths are always present in the state
            state.subpaths.remove(p);
            state.saveState();
        end

        function b = isProject(obj, projectFolder)
            b = exist(fullfile(projectFolder, obj.projectFileName), 'file');
        end

        % Read project file in a folder and return cell array of all subpaths
        function sp = projectSubpaths(obj, projectFolder)
            try
                fstr = fileread(fullfile(projectFolder, obj.projectFileName));
            catch
                error('Subpath definition file ''%s'' not found.', obj.projectFileName);
            end
            sp = splitlines(strtrim(fstr));
        end

        function s = loadedSubpaths(obj)
            state = mpm.PersistentState(obj.stateFilePath);
            s_unordered = state.subpaths.keys();

            mpath = obj.matlabPath();
            [ok, I] = ismember(s_unordered, mpath);
            s = mpath(sort(I));
        end

        function ps = matlabPath(obj)
            ps = split(path(), pathsep);
        end

        % Load all subpaths for a project
        function checkin(obj, projectFolder)
            sp = obj.projectSubpaths(projectFolder);

            for i = 1:length(sp)
                fullSubpath = fullfile(projectFolder, sp{i});
                obj.loadSubpath(fullSubpath);
            end
        end

        % Unload all subpaths for a project
        function checkout(obj, projectFolder)
            sp = obj.projectSubpaths(projectFolder);

            for i = 1:length(sp)
                fullSubpath = fullfile(projectFolder, sp{i});
                obj.unloadSubpath(fullSubpath);
            end
        end

        % Check if the projects settings are correct
        % Is the project file correct?
        % The right version is in the matlab path
        % The right version is at the top of the path
        function b = verify(obj, projectFolder)
            mpath = obj.matlabPath();
            sp = obj.projectSubpaths(projectFolder);

            for i = 1:length(sp)
                if ~obj.subpathIsActive(fullfile(projectFolder,sp{i}), mpath)
                    b = false;
                    return;
                end
            end
            b = true;
        end

        % Unload all loaded subpaths. The matlab path should be returned to it's original state
        function clear(obj)
            sp = obj.loadedSubpaths();

            for i = 1:length(sp)
                obj.unloadSubpath(sp{i});
            end
        end
    end

    methods (Static)
        % Test if the path p is active among folders in path with the same final foldername
        function b = subpathIsActive(p, paths)
            pathparts = split(p, filesep);
            foldername = pathparts{end};

            for i = 1:length(paths)
                if ~endsWith(paths{i}, foldername)
                    % Ignore files with different end foldername
                    continue
                end

                b = strcmp(p, paths{i});
                return
            end

            b = false;
        end
    end
end

% TODO: Organize order of methods
% TODO: Add flag for being persistent or not? (savepath or not)
