/*
 * Copyright 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gradle.foundation.output;

import org.gradle.foundation.output.definitions.*;

import java.util.List;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 This parses gradle's output text looking for links to files. We use RegEx to
 do the bulk of  the matching. However, we want this to be 'pluggable' (to a
 degree) so new file links can be added easily. To accomplish this, this works
 with FileLinkDefinitions. They require a basic RegEx pattern to match some
 initial part of the a file link, however, they can be implemented to do more
 advanced parsing of the text. The definitions are built-up and held by
 FileLinkDefinitionLord. This just handles the tedium of matching the
 all-inclusive pattern with text, managing indices, and calling the matching
 FileLinkDefinition to refine the match.

 @author mhunsicker
 */
public class OutputParser
{
    private FileLinkDefinitionLord fileLinkDefinitionLord;
    private boolean verifyFileExists;   //this is really only for testing where the file will not exist.

    public OutputParser( FileLinkDefinitionLord fileLinkDefinitionLord, boolean verifyFileExists )
   {
       this.fileLinkDefinitionLord = fileLinkDefinitionLord;
       this.verifyFileExists = verifyFileExists;
   }

    public boolean isVerifyFileExists() { return verifyFileExists; }
    public FileLinkDefinitionLord getFileLinkDefinitionLord() { return fileLinkDefinitionLord; }

   /**
    This parses the text looking for file links
    @param text the text to parse
    @return a list of FileLinks for each file that was found in the text.
    */
   public List<FileLink> parseText( String text )
   {
      List<FileLink> fileLinks = new ArrayList<FileLink>();

      Pattern combinedSearchPattern = fileLinkDefinitionLord.getSearchPattern();
      Matcher matcher = combinedSearchPattern.matcher( text );

      int index = 0;

      boolean foundAMatch = matcher.find( index );
      while( foundAMatch )
      {
         // Retrieve matching string
         String matchedText = matcher.group();

         // Retrieve indices of matching string
         int start = matcher.start();
         int end = matcher.end();

         int nextStarting = start;

         //now that we have a match, we have to find the one FileLinkDefinition that actually matches so it
         //can determine the actual file. This makes the matcher more plugable.
         FileLinkDefinition fileLinkDefinition = fileLinkDefinitionLord.getMatchingFileLinkDefinition( matchedText );
         if( fileLinkDefinition != null )
         {
            nextStarting = fileLinkDefinition.parseFileLink( text, matchedText, start, end, verifyFileExists, fileLinks );
         }
         else
         {
            //this is probably a serious problem that needs to be reported. However, we'll continue as if nothing bad happened.
            System.out.println( "We found a match but didn't find the matching definition. Matched text:\n" + text );
         }

         if( nextStarting == -1 || nextStarting < start )
         {
            nextStarting = start;
         }

         index = nextStarting + 1;
         if( index < text.length() )
         {
            foundAMatch = matcher.find( index );
         }
         else
         {
            foundAMatch = false; //don't continue searching if we've found the end
         }
      }

      return fileLinks;
   }
}


