%
%PARSECHORD parse chord symbol to its constituent string elements
% 
% [rootnote, shorthand, degreelist,
%              bass, success, errormessage] = parsechord(chord, {verbose})
% 	
% Parses the chord symbol string "chord" into its separate string elements. 
% 
% NOTE: This function does not check validity of the chord elements, it
% merely separates them according to the chord syntax. 
% To obtain chord elements with included validity check use GETCHORDINFO.
%
% Success = 1 if symbols parsed correctly, 0 otherwise.
%
% If optional argument 'verbose' is 1, function prints any errormessage to 
% the screen.
% 	
% returns:  rootnote        (note string)
%           shorthand       (shorthand string)
%           degreelist      (degree-list string)
%           bass            (degree string)
%           success         (boolean)
%           errormessage    (string)
%
% See also: getchordinfo                     
%
% Author: Christopher Harte,  August 2005
% 
% Copyright: Centre for Digital Music, Queen Mary University of London 2005 
%
% This file is part of the C4DM Chord Toolkit.  
%
% The C4DM Chord Toolkit is free software; you can redistribute it and/or 
% modify it under the terms of the GNU General Public License as published 
% by the Free Software Foundation; either version 2 of the License, or
% (at your option) any later version.
%
% The C4DM Chord Toolkit 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 General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with the C4DM Toolkit; if not, write to the Free Software
% Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

%		
function  [rootnote, shorthand, degrees, bass, success, errormessage] = parsechord(chord, verbose)
	
% set verbose default to 0
if nargin <2
    verbose = 0;
end

ilength = length(chord);

% initialise variables
errormessage = '';
rootnote = '';
shorthand = '';
degrees = '';
bass = '';

success = 1;
index = 1;


% check for 'no chord' symbol
if chord(index) == 'N'
    rootnote = chord(index);
    index = index +1;
    % check to see there are no further characters
    if(index<=ilength)
        errormessage = sprintf(['Error in parsechord: \nExtra characters after "no chord" symbol "' chord '"\n']); 
        success = 0;
    end
else
% parse thechord symbol

    % the first part of the symbol before a switch character should be the root note
    while ((index <=ilength ))
    
        if (chord(index) ==  ':') || (chord(index) ==  '/') || ...
           (chord(index) ==  '(') || (chord(index) ==  ')')
            
            break
        end

        rootnote(index) = chord(index);
        index = index+1;
    
        if(index > ilength) || (chord(index) == '/')
            % if chord is a rootnote on its own or with just a bass note 
            % then it is a major chord therefore set shorthand to 'maj'
            shorthand = 'maj';
        end
    end
    
    % initialise booleans to record which switch characters we have found
    colon = 0;
    openbracket = 0;
    closebracket = 0;
    slash = 0;
    
    % parse the rest of the chord symbol
    while(index <= ilength)
        
        % reset temporary index 
        tempindex = 1;
        
       switch(chord(index))
       
           case ':'  
                        
               % if we find a colon after any switch characters have 
               % already occured then the symbol is incorrect 
               if (colon || openbracket || closebracket || slash)
                    errormessage = sprintf(['Error in parsechord: \nIncorrect character sequence in chord "' chord '"\n']); 
                    success = 0;
                    index = ilength+1;
               else
                
                   % found the first instance of a colon character
                   colon = 1;
                   
                   index = index +1;
                   
                   if(index > ilength)
                        errormessage = sprintf(['Error in parsechord: \nFound ":" at end of chord string "' chord '"\n']); 
                        success = 0; 
                   end
                   % colon should be followed by a shorthand string or
                   % a degree list contained in brackets
                   while (index <= ilength)
                       
                       if (chord(index) ==  ':') || (chord(index) ==  '/') || ...
                          (chord(index) ==  '(') || (chord(index) ==  ')')

                           break
                       end

                       % copy character into shorthand
                       shorthand(tempindex) = chord(index);
                       index = index +1;
                       tempindex = tempindex +1;

                   end
               
               end
               
           case '('
               
               % if we have had a colon but no other switch charaters then
               % an open bracket signifies the start of the degree list
               if (colon && ~slash && ~closebracket && ~openbracket) 
                   
                   openbracket = 1;
                   
                   index = index +1;
                   
                   while (index <= ilength)

                       if (chord(index) ==  ':') || (chord(index) ==  '/') || ...
                          (chord(index) ==  '(') || (chord(index) ==  ')')

                           break
                       end
                       
                       % copy character into degrees
                       degrees(tempindex) = chord(index);
                       index = index +1;
                       tempindex = tempindex +1;

                   end
                   
                   if(index > ilength)
                        errormessage = sprintf(['Error in parsechord: \nDegree list brackets not closed in chord "' chord '"\n']); 
                        success = 0; 
                   end
                   
               else
                   errormessage = sprintf(['Error in parsechord: \nIncorrect character sequence in chord "' chord '"\n']); 
                   success = 0;
                   index = ilength +1;
               end
               
           case ')'
               
               
               % if we find a closing bracket then we should either be at
               % the end of the symbol or there should be a slash to follow
               if (colon && openbracket && ~slash && ~closebracket) 
                   
                   closebracket = 1;
                   index = index +1;
                   
               else
                   errormessage = sprintf(['Error in parsechord: \nIncorrect character sequence in chord "' chord '"\n']); 
                   success = 0;
                   index = ilength +1;
               end
               
               % check to see that the brackets contained something
               if isempty(degrees)
                   errormessage = sprintf(['Error in parsechord: \nBrackets contain no degrees in chord "' chord '"\n']); 
                   success = 0;
                   index = ilength +1;
               end
               
               
           case '/'
               % forward slash should be followed by a degree string
              
                   slash = 1;

                   % move on to next character to process the expected bass degree
                   index = index +1;
                                     
                   % check that we haven't overun the end of the symbol string
                   if(index > ilength)
                        errormessage = sprintf(['Error in parsechord: \nNo bass degree "/" at end of chord "' chord '"\n']); 
                        success = 0; 
                   end
                   
                   % check that if we have had an open bracket that it also
                   % had a closed bracket
                   if(xor(openbracket,closebracket))
                        errormessage = sprintf(['Error in parsechord: \nFound "/" before closing bracket in chord "' chord '"\n']); 
                        success = 0; 
                        index = ilength +1;
                   end

                   % check that the previous character was not a ':'
                   if(chord(index-2) == ':')
                        errormessage = sprintf(['Error in parsechord: \nFound "/" directly after ":" in chord "' chord '"\n']); 
                        success = 0; 
                        index = ilength +1;
                   end

                   
                   while( index <= ilength )
                       
                       % if we find a switch character after a slash then
                       % the symbol is incorrect
                       if (chord(index) ==  ':') || (chord(index) ==  '/') || ...
                          (chord(index) ==  '(') || (chord(index) ==  ')')
         
                           errormessage = sprintf(['Error in parsechord: \nIncorrect character sequence in chord "' chord '"\n']); 
                           success = 0;
                           index = ilength +1;
                       
                       else

                           % copy remaining characters into bass
                           bass(tempindex) = chord(index);
                           index = index +1;
                           tempindex = tempindex +1;
                       end
                   end
                            
           otherwise
                errormessage = sprintf(['Error in parsechord: \nUnrecognised chord "' chord '"\n']); 
                success = 0;
                index = ilength + 1;
       end
            
    end
end            
            
if (verbose == 1) && (success == 0)
    fprintf(1,errormessage);
end
        

