function tagStruct = getTags(fileList, optionStruct)
% get Tag informations from a file list
%
% THIS IS NO USER FUNCTION

% MDT: MATLAB developement toolbox - for the everyday programmer
%   Copyright (C) 2013 Alexander Reinhold
%
% This program is free software: you can redistribute it and/or modify it 
%   under the terms of the GNU Lesser General Public License as published 
%   by the Free Software Foundation, either version 3 of the License, or 
%   (at your option) any later version.
%
% This program is distributed in the hope that it will be useful, but 
%   WITHOUT ANY WARRANTY; without even the implied warranty of 
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
%   Lesser General Public License for more details.
%
% You should have received a copy of the GNU Lesser General Public License 
%   along with this program.  If not, see <http://www.gnu.org/licenses/>.

%% input/option handling
if ~iscell(fileList)
    fileList = {fileList};
end

%% initialize output
tagStruct.mfile = [];
tagStruct.line = [];
tagStruct.lineEnd = [];
tagStruct.type = [];
tagStruct.comment = cell(0,1);

for iFile = 1:length(fileList)    
    %% read file
    fid = fopen(fileList{iFile}, 'r');
    fileContent = fread(fid, inf, 'uint8=>char')';
    fclose(fid);
    % strip carriage return
    fileContent(fileContent==13) = [];
    % add final empty line
    fileContent = [fileContent char(10)];
    
    %% line index information
    % newline indices  to fileContent
    newLineIndexVector = [0 find(fileContent == char(10))];
    % openening tag lines
    openTagIndexVector = regexp(fileContent, ...
        ['\n' optionStruct.tagOpen]) + 1;
    openTagLine = 0*openTagIndexVector;
    for iOpen = 1:length(openTagIndexVector)
        openTagLine(iOpen) = sum(...
            newLineIndexVector <= openTagIndexVector(iOpen));
    end
    % closing tag lines
    closeTagIndexVector = regexp(fileContent, [optionStruct.tagClose '\n']);
    closeTagLine = 0*closeTagIndexVector;
    for iClose = 1:length(closeTagLine)
        closeTagLine(iClose) = sum(...
            newLineIndexVector <= closeTagIndexVector(iClose));
    end
    % start and end of comment blocks
    commentIndexVector = regexp(fileContent, '\n\s*%') + 1;
    commentLine = 0*commentIndexVector;
    for iCom = 1:length(commentIndexVector)
        commentLine(iCom) = sum(...
            newLineIndexVector <= commentIndexVector(iCom));
    end
    filter = double(newLineIndexVector == 0);
    filter(commentLine) = 1;
    filter = diff([filter 0]);
    openCommentLine = find(filter == 1) + 1;
    closeCommentLine = find(filter == -1);
   
    %% initialize local temp struct
    thisTagStruct.mfile = repmat(iFile, 1, length(openTagLine));
    thisTagStruct.line = zeros(1, length(openTagLine));
    thisTagStruct.lineEnd = zeros(1, length(openTagLine));
    thisTagStruct.type = zeros(1, length(openTagLine));
    thisTagStruct.comment = cell(1, length(openTagLine));
    
    %% identify tags
    for iOpen = 1:length(openTagLine)
        % set begining index for tag-content
        %   this is always the index in openIndexVector + 1. at least for
        %   linux systems. in windows systems it might be +2 since lines
        %   are ended by '\n\r' or char([10 13])
        startLine = openTagLine(iOpen);
        thisTagStruct.line(iOpen) = startLine;
        
        % find next closing index
        nextCloseLine = closeTagLine(find(...
            closeTagLine > startLine, 1, 'first'));
        
        % set ending line for tag-content is
        %   1) all the following comment lines when there is:
        %      1a) no remaining closing tag
        %      1b) the next closing tag line comes BEFORE the next opening
        %          line
        %   2) all lines until the closing comment line, otherwise
        if isempty(nextCloseLine) || (...
                iOpen < length(openTagLine) && ...
                nextCloseLine > openTagLine(iOpen+1)...
                )
            % we have to select the upcoming closing tag
            endLine = closeCommentLine(...
                sum(closeCommentLine < startLine) + 1);
        else
            endLine = nextCloseLine;
        end        
        % find end line number
        thisTagStruct.lineEnd(iOpen) = endLine;
        
        % set comment content
        thisTagStruct.comment{iOpen} = fileContent(...
            (newLineIndexVector(startLine)+1):...
            (newLineIndexVector(endLine+1)-1));
        
        % get tag name
        tagNameStartIndex = regexp(thisTagStruct.comment{iOpen}, '\w+:');
        tagNameStopIndex = regexp(thisTagStruct.comment{iOpen}, ':');
        if isempty(tagNameStartIndex) || isempty(tagNameStopIndex) || ...
            tagNameStartIndex(1) >= tagNameStopIndex(1)
            tagName = '';
        else
            tagName = thisTagStruct.comment{iOpen}(tagNameStartIndex(1):tagNameStopIndex(1)-1);
        end
        
        % identify tag type
        tagType = 1;
        if ~isempty(tagName)
            for iTagName = 1:length(optionStruct.knownTags)
            for jTagName = 1:length(optionStruct.knownTags{iTagName})
                if strcmpi(tagName, optionStruct.knownTags{iTagName}{jTagName})
                    tagType = iTagName;
                    break
                end
            end
            end
        end
        % restrict tag types
        if any(optionStruct.restrictedTags == tagType)
            tagType = -tagType;
        end
        % set tag type
        thisTagStruct.type(iOpen) = tagType;
    end
    
    %% append tag information
    filter = thisTagStruct.type ~= 0;
    tagStruct.mfile = [tagStruct.mfile thisTagStruct.mfile(filter)];
    tagStruct.line = [tagStruct.line thisTagStruct.line(filter)];
    tagStruct.lineEnd = [tagStruct.lineEnd thisTagStruct.lineEnd(filter)];
    tagStruct.type = [tagStruct.type thisTagStruct.type(filter)];
    tagStruct.comment = [tagStruct.comment thisTagStruct.comment(filter)];
    
end