require 'file/tail'
# @author Sylvain Desbureaux
module EasyLogger
  # = Class EasyLogger which will do all the job of matching the lines and showing it
  # @author Sylvain Desbureaux
  # 
  # == Attributes
  # @attr_reader [String]     log    the log file/stream that we'll look into
  # @attr_reader [Array]   classes   the classes to match in the log
  # @attr_reader [Array]   methods   the methods to match in the log
  # @attr_reader [Boolean] verbose   the verbosity of the output
  # @attr_reader [String]  separator the separator of the values in the log. it's "|" per default
  # 
  # == Use
  # There's two methods to use the class: file() and tail().
  # Both of them use the same parameters (the attributes of the class).
  # The log file need to be correctly formatted. the parser assumes that the log file has this format (with '|' as separator in the example):
  #     |some tag (example: the date and time)|some tag|...|class|method| free text
  # The Separator could be at the end of the free text but never in the middle so choose it carefully!
  # @see Logging a working logger example
  # 
  # === File
  # File will open the stream, parse and output the matching line and exit
  #
  # === Tail
  # Tail will open the end of the stream and parse and output the matching line and exit
  
  class EasyLogger
    
    # Display the relevant line of the log matching the classes/methods
    #
    # @param [String]  log       the log file/stream that we'll look into
    # @param [String]   classes   the classes to match in the log
    # @param [String]   methods   the methods to match in the log
    # @param [Boolean] verbose   the verbosity of the output
    # @param [String]  separator the separator of the values in the log. it's "|" per default
    def file(log, classes, methods, verbose, separator='|')
      result = String.new
      puts "Looking in #{log} I/O for line matching #{classes} classe(s) and #{methods} method(s) with separator '#{separator}'" if verbose && !classes.nil? && !methods.nil?
      puts "Looking in #{log} I/O for line #{methods} method(s) with separator '#{separator}'" if verbose && classes.nil? && !methods.nil?
      puts "Looking in #{log} I/O for line matching #{classes} classe(s) with separator '#{separator}'" if verbose && !classes.nil? && methods.nil?
      puts "Looking in #{log} I/O for all lines" if verbose && classes.nil? && methods.nil?
      classes =classes.split(',') unless classes.nil?
      methods =methods.split(',') unless methods.nil?
      classes.map!{|c| c.downcase} unless classes.nil?
      methods.map!{|m| m.downcase} unless methods.nil?
      line_number = 0
      line_match = 0
      IO.foreach(log) do |line| 
        line_number += 1
        if match(line,classes,methods, separator)
          puts line 
          line_match += 1
        end
        
      end
      puts "#{line_match} lines matched on the #{line_number} lines in the log" if verbose
    end
    
    # Display the relevant <u>new</u> lines of the log matching the classes/methods
    #
    # @param (see EasyLogger#file)
    def tail(log, classes, methods, verbose, separator='|')
      result = String.new
      puts "Tailing in #{log} I/O for line matching #{classes} classe(s) and #{methods} method(s) with separator '#{separator}'" if verbose && !classes.nil? && !methods.nil?
      puts "Tailing in #{log} I/O for line #{methods} method(s) with separator '#{separator}'" if verbose && classes.nil? && !methods.nil?
      puts "Tailing in #{log} I/O for line matching #{classes} classe(s) with separator '#{separator}'" if verbose && !classes.nil? && methods.nil?
      puts "Tailing in #{log} I/O for all lines" if verbose && classes.nil? && methods.nil?
      classes =classes.split(',') unless classes.nil?
      methods =methods.split(',') unless methods.nil?
      classes.map!{|c| c.downcase} unless classes.nil?
      methods.map!{|m| m.downcase} unless methods.nil?
      @line_number = 0
      @line_match = 0
      #      interrupted = false
      trap("INT") do
        puts "#{@line_match} lines matched on the #{@line_number} lines in the log" if verbose 
        break
      end
      begin
        File::Tail::Logfile.tail(log) do |line|
          @line_number += 1
          if match(line,classes,methods, separator)
            puts line 
            @line_match += 1
          end
          #        if interrupted
          #          break
          #        end
        end
      rescue
        nil
      end
    end
    
    # Verify if a line match the classes/method
    #
    # @private
    # @param [String] line the line to match the regexp
    # @param classes (see EasyLogger#file)
    # @param methods (see EasyLogger#file)
    # @param separator (see EasyLogger#file)
    # @return [Boolean] True if the line is matched, false if not 
    @private 
    def match(line,classes,methods,separator)
      unless line[0..-2].split(separator).length < 3
        methode = line[0..-2].split(separator)[-2].strip.downcase
        classe = line[0..-2].split(separator)[-3].strip.downcase
      
        (classes.nil? || classes.include?(classe)) && (methods.nil? || methods.include?(methode))
      else
        false
      end
    end
  end
end