require 'digest/md5'

module Keystone::Batch
  #
  # = バッチをざくっと書きたい時用モジュール
  # 
  # 基本的にはexecuteメソッドにバッチ処理をブロックで渡す
  #
  # == 定数
  #
  # ERROR_MAIL_TO (エラーメール送信先) 設定しておけば自動でエラーメールを送信してくれる
  #
  # ERROR_MAIL_FROM (エラーメール送信元) 設定されてない場合はERROR_MAIL_TOを使用
  #
  # ERROR_MAIL_STMP_ADDR (エラーメール送信SMTPアドレス) 設定されてない場合は"127.0.0.1"
  #
  # ERROR_MAIL_STMP_PORT (エラーメール送信SMTPポート) 設定されてない場合は25
  #
  module Base
    # [double_process_check]
    #   2重起動チェック
    # [auto_recover]
    #   2重起動チェック用のpidファイルがすでに存在しているがプロセスは見あたらない
    #   そんな時、そのまま実行を続けるかどうか
    #
    #
    # バッチの主処理をこのメソッドへのブロック引数として定義してください
    #
    #   require 'rubygems'
    #   require 'keystone'
    #
    #   include Keystone::Batch::Base
    #
    #   # ERROR_MAIL_TOを設定しておけば自動でエラーメールが送信される
    #   ERROR_MAIL_TO = ARGV[0]
    #
    #   execute() do
    #     info "batch process01"
    #   end
    #
    def execute(double_process_check = true,auto_recover = true,&process)
      info "start script(#{File.expand_path($0)})"
      warn "ERROR_MAIL_TO not defined.if you want error mail automatically,set this value." unless Module.constants.include?("ERROR_MAIL_TO")
      script_started_at = Time.now
      double_process_check_worked = false
      begin
        # double process check
        if double_process_check
          pg_path = File.expand_path($0)
          pg_name = File.basename(pg_path)
          hash = Digest::MD5.hexdigest(pg_path)
          pid_file = "/tmp/.#{pg_name}.#{hash}.pid"
          debug pid_file
          if File.exists?(pid_file)
            pid = File.open(pid_file).read.chomp
            pid_list = `ps -e | awk '{print $1}'`
            if pid_list =~ /#{pid}/
              warn "pid:#{pid} still running"
              double_process_check_worked = true
              return nil
            else
              if auto_recover
                warn "pid file still exists,but process does not found.so process continues"
              else
                double_process_check_worked = true
                raise "pid file still exists,but process does not found"
              end
            end
          end
          File.open(pid_file, "w"){|file|
            file.write $$
          }
        end
        return (yield process)
      rescue => e
        error e
        send_error_mail(e)
      ensure
        unless double_process_check_worked
          File.delete(pid_file) if double_process_check
        end
        info "finish script (%1.3fsec)" % (Time.now - script_started_at)
      end
    end
    
    #
    # = エラーメール送信メソッド
    #  エクセプションを何も考えずにメールにて送信する
    #  各種メール送信属性は定数にて渡す
    #
    # [exception]
    #   エクセプションクラスインスタンス
    #
    def send_error_mail(exception)
      if Module.constants.include?("ERROR_MAIL_TO")
        host = Keystone::Os.get()
        title = %|error occur at "#{host.hostname}" [#{error.message}]|
        
        mail_to = ERROR_MAIL_TO
        mail_to = [mail_to] if mail_to.is_a?(String)
        mail_from = Module.constants.include?("ERROR_MAIL_FROM") ?  ERROR_MAIL_FROM : mail_to[0]
        smtp_addr = Module.constants.include?("ERROR_MAIL_STMP_ADDR") ?  ERROR_MAIL_STMP_ADDR : '127.0.0.1'
        smtp_port = Module.constants.include?("ERROR_MAIL_STMP_PORT") ?  ERROR_MAIL_STMP_PORT : 25
        
        body  = <<-BODY
==== error message ====
#{exception.message}
====== backtrace ======
#{exception.backtrace.join("\n")}
===== environment =====
#{host.dump}
BODY
        Keystone::Mail::Send.send(mail_from,mail_to,title,body,smtp_addr,smtp_port)
      end
    end
  end
end