# encoding: UTF-8
require 'ipaddr'
require 'resolv'
require 'socket'
require 'mauve/mauve_time'

module Mauve 
  class Sender
    DEFAULT_PORT = 32741
    include Resolv::DNS::Resource::IN
    
    def initialize(*destinations)
      destinations = destinations.flatten
      
      destinations = begin
        File.read("/etc/mauvealert/mauvesend.destination").split(/\s+/)
      rescue Errno::ENOENT => notfound
        []
      end if destinations.empty?
      
      if !destinations || destinations.empty?
        raise ArgumentError.new("No destinations specified, and could not read any destinations from /etc/mauvealert/mauvesend.destination")
      end
    
      @destinations = destinations.map do |spec|
        next_spec = begin
          # FIXME: for IPv6
          port = spec.split(":")[1] || DEFAULT_PORT
          IPAddr.new(spec.split(":")[0])
          ["#{spec}:#{port}"]
        rescue ArgumentError => not_an_ip_address
          Resolv::DNS.open do |dns|
            srv_spec = spec[0] == ?_ ? spec : "_mauvealert._udp.#{spec}"
            list = dns.getresources(srv_spec, SRV).map do |srv|
              srv.target.to_s + ":#{srv.port}"
            end
            list = [spec] if list.empty?
            list.map do |spec2|
              spec2_addr, spec2_port = spec2.split(":")
              spec2_port ||= DEFAULT_PORT
              dns.getresources(spec2_addr, A).map do |a|
                "#{a.address}:#{spec2_port}"
              end
            end
          end
        end.flatten

        error "Can't resolve destination #{spec}" if next_spec.empty?

        next_spec
      end.
      flatten.
      uniq
    end
    
    def send(update, verbose=0)

      #
      # Must have a source, so default to hostname if user doesn't care 
      update.source ||= `hostname -f`.chomp
      
      #
      # Make sure all alerts default to "-r now"
      #
      update.alert.each do |alert|
        next if alert.raise_time || alert.clear_time
        alert.raise_time = MauveTime.now.to_i
      end
     
      #
      # Make sure we set the transmission time
      #
      update.transmission_time = MauveTime.now.to_i 

      data = update.serialize_to_string


      if verbose == 1
        print "#{update.transmission_id}\n"
      elsif verbose >= 2
        print "Sending #{update.inspect.chomp} to #{@destinations.join(", ")}\n"
      end

      @destinations.each do |spec|
        UDPSocket.open do |sock|
          ip = spec.split(":")[0]
          port = spec.split(":")[1].to_i
          sock.send(data, 0, ip, port)
        end
      end
    end
  end
end