#
# Fluentd
#
#    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.
#

module Fluent
  class Match
    def initialize(pattern_str, output)
      patterns = pattern_str.split(/\s+/).map {|str|
        MatchPattern.create(str)
      }
      if patterns.length == 1
        @pattern = patterns[0]
      else
        @pattern = OrMatchPattern.new(patterns)
      end
      @output = output
    end

    attr_reader :output

    def emit(tag, es)
      chain = NullOutputChain.instance
      @output.emit(tag, es, chain)
    end

    def start
      @output.start
    end

    def shutdown
      @output.shutdown
    end

    def match(tag)
      if @pattern.match(tag)
        return true
      end
      return false
    end
  end

  class MatchPattern
    def self.create(str)
      if str == '**'
        AllMatchPattern.new
      else
        GlobMatchPattern.new(str)
      end
    end
  end

  class AllMatchPattern < MatchPattern
    def match(str)
      true
    end
  end

  class GlobMatchPattern < MatchPattern
    def initialize(pat)
      stack = []
      regex = ['']
      escape = false
      dot = false

      i = 0
      while i < pat.length
        c = pat[i,1]

        if escape
          regex.last << Regexp.escape(c)
          escape = false
          i += 1
          next

        elsif pat[i,2] == "**"
          # recursive any
          if dot
            regex.last << "(?![^\\.])"
            dot = false
          end
          if pat[i+2,1] == "."
            regex.last << "(?:.*\\.|\\A)"
            i += 3
          else
            regex.last << ".*"
            i += 2
          end
          next

        elsif dot
          regex.last << "\\."
          dot = false
        end

        if c == "\\"
          escape = true

        elsif c == "."
          dot = true

        elsif c == "*"
          # any
          regex.last << "[^\\.]*"

          # TODO
          #elsif c == "["
          #  # character class
          #  chars = ''
          #  while i < pat.length
          #    c = pat[i,1]
          #    if c == "]"
          #      break
          #    else
          #      chars << c
          #    end
          #    i += 1
          #  end
          #  regex.last << '['+Regexp.escape(chars).gsub("\\-",'-')+']'

        elsif c == "{"
          # or
          stack.push []
          regex.push ''

        elsif c == "}" && !stack.empty?
          stack.last << regex.pop
          regex.last << Regexp.union(*stack.pop.map {|r| Regexp.new(r) }).to_s

        elsif c == "," && !stack.empty?
          stack.last << regex.pop
          regex.push ''

        elsif c =~ /[a-zA-Z0-9_]/
          regex.last << c

        else
          regex.last << "\\#{c}"
        end

        i += 1
      end

      until stack.empty?
        stack.last << regex.pop
        regex.last << Regexp.union(*stack.pop).to_s
      end

      @regex = Regexp.new("\\A"+regex.last+"\\Z")
    end

    def match(str)
      @regex.match(str) != nil
    end
  end

  class OrMatchPattern < MatchPattern
    def initialize(patterns)
      @patterns = patterns
    end

    def match(str)
      @patterns.any? {|pattern| pattern.match(str) }
    end
  end
end
