#!/usr/bin/env ruby
#
# Copyright (C) 2008  Kouhei Sutou <kou@cozmixng.org>
#
# This program 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 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

require 'English'
require 'pathname'
require 'fileutils'
require 'time'
require 'optparse'
require 'erb'

base = Pathname.new(__FILE__).dirname.expand_path
top = (base + "..").expand_path
$LOAD_PATH.unshift((top + "tool" + "ruby").to_s) #FIX Path
require 'milter-rrd'

class MilterLogTool
  include ERB::Util

  def initialize
    @log = ARGF
    @update_db = true
    @sessions = nil
    @mail_status = nil
    @stops = nil
    @output_graphs = []
  end

  def parse_options(argv)
    opts = OptionParser.new do |opts|
      opts.on("--log=LOG_FILENAME",
              "The log file name in which is stored Milter log",
              "(STDIN)") do |log|
        @log = File.open(log)
      end

      opts.on("--rrd-directory=DIRECTORY") do |directory|
        @rrd_directory = directory
        FileUtils.mkdir_p(@rrd_directory) unless File.exist?(@rrd_directory)
      end

      opts.on("--[no-]update-db",
              "Update RRD database with log file",
              "(#{@update_db})") do |boolean|
        @update_db = boolean
      end
    end
    opts.parse!(argv)
  end

  def update
    return unless @update_db

    listeners = [sessions, mail_status, stops]
    @log.each_line do |line|
      case line
      when /\A(\w{3} \d+ \d+:\d+:\d+) [\w\-]+ ([\w\-]+)\[\d+\]: /
        time_stamp = $1
        name = $2
        content = $POSTMATCH
        next unless name == "milter-manager"
        if /\A\[statistics\] / =~ content
          content = $POSTMATCH.chomp
        else
          next
        end
        time_stamp = Time.parse(time_stamp).utc
        listeners.each do |listener|
          listener.feed(time_stamp, content)
        end
      else
      end
    end

    listeners.each do |listener|
      listener.update
    end
  end

  def output_graph(time_span, label=nil, options={})
    output_graphs = []
    [sessions, mail_status, stops].each do |generator|
      file_name = generator.output_graph(Milter::RRD::TimeSpan.new(time_span),
                                         options)
      output_graphs << file_name if file_name
    end
    @output_graphs << [label || time_span, output_graphs]
  end

  def output_all_graph
    output_graph("minute", "Last Day",
                 :start_time => -3600 * 24,
                 :graph_tag => "day")
    output_graph("minute", "Last Week",
                 :start_time => -3600 * 24 * 7,
                 :graph_tag => "week")
    output_graph("minute", "Last Month",
                 :start_time => -3600 * 24 * 31,
                 :graph_tag => "month")
    output_graph("minute", "Last Year",
                 :start_time => -3600 * 24 * 365,
                 :graph_tag => "year")
  end

  def output_html
    File.open(File.join(@rrd_directory, "index.html"), "w") do |html|
      html.print(html_header)
      html.print(html_index)
      html.print(html_graphs)
      html.print(html_footer)
    end
  end

  private
  def now
    @now ||= Time.now.utc
  end

  def sessions
    @sessions ||= Milter::RRD::Session.new(@rrd_directory, now)
  end

  def mail_status
    @mail_status ||= Milter::RRD::MailStatus.new(@rrd_directory, now)
  end

  def stops
    @stops ||= Milter::RRD::Stops.new(@rrd_directory, now)
  end

  def html_header
    <<-EOH
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
    <meta http-equiv="Refresh" content="300" />
    <meta http-equiv="Pragma" content="no-cache" />
    <title>milter-manager statistics</title>
  </head>

  <body>
    <h1>milter-manager statistics</h1>
EOH
  end

  def html_index
    result = "<ul>\n"
    @output_graphs.each do |time_span, graphs|
      next if graphs.empty?
      result << %Q[  <li><a href="\##{h(time_span)}">#{h(time_span)}</a></li>\n]
    end
    result << "</ul>\n"
    result
  end

  def html_graphs
    result = ""
    @output_graphs.each do |time_span, graphs|
      next if graphs.empty?
      result << %Q[<h2 id=\"#{h(time_span)}\">#{h(time_span)}</h2>\n]
      graphs.each do |graph|
        result << %Q[  <p><img src="#{h(File.basename(graph))}" /></p>\n]
      end
      result << "\n"
    end
    result
  end

  def html_footer
    <<-EOF
  </body>
</html>
EOF
  end
end

milter_log_tool = MilterLogTool.new
milter_log_tool.parse_options(ARGV)
milter_log_tool.update
milter_log_tool.output_all_graph
milter_log_tool.output_html

# vi:ts=2:nowrap:ai:expandtab:sw=2
