# encoding: utf-8
#
# This file is part of the akaer gem. Copyright (C) 2012 and above Shogun <shogun_panda@me.com>.
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
#

require "cowtech-lib"
require "ipaddr"

class Fixnum
  def i(num = 3)
    self.to_s.rjust(num, "0")
  end
end

module Akaer
  class Application < Cowtech::Lib::Script
    def self.start
      self.new
    end

    def initialize
      super(:args => {:name => "Interface aliases", :version => 1.0, :description => "A small utility to add aliases to network interfaces.", :usage => "Usage: #{ARGV[0]} [OPTIONS] [add|remove]"})
    end

    def add_options
      @options_parser << [
        {:name => "configuration", :short => "-c", :long => "--configuration", :type => :string, :help => "Configuration file. By default is the standard output", :meta => "FILE", :required => false, :default => "~/.akaer_config"},
        {:name => "interface", :short => "-i", :long => "--interface", :type => :string, :help => "Interface", :meta => "INTERFACE", :required => false, :default => "lo0"},
        {:name => "addresses", :short => "-A", :long => "--addresses", :type => :list, :help => "Addresses to add", :required => false, :default => []},
        {:name => "start-address", :short => "-s", :long => "--start-address", :type => :string, :help => "Starting address (not used if --addresses is specified).", :meta => "ADDRESS", :required => false, :default => "10.0.0.1"},
        {:name => "aliases", :short => "-S", :long => "--aliases", :type => :int, :help => "Number of aliases to add", :meta => "NUMBER", :required => false, :default => 5},
        {:name => "log-file", :short => "-l", :long => "--log-file", :type => :string, :help => "Log file. By default is the standard output", :meta => "FILE", :required => false, :default => "STDOUT"},
        {:name => "log-level", :short => "-L", :long => "--log-level", :type => :int, :help => "The default log level. Valid values are from 0 to 5 where 0 means \"all messages\".", :meta => "NUMBER", :required => false, :default => Logger::INFO},
        {:name => "dry-run", :short => "-n", :long => "--dry-run", :type => :bool, :help => "Do not really execute actions."},
        {:name => "quiet", :short => "-q", :long => "--quiet", :type => :bool, :help => "Do not show any message", :required => false, :default => false}
      ]
    end

    def execute_command(interface, action, ip, prefix = "")
      prefix += " " if !prefix.blank?
      @logger.info("#{prefix}#{(action == "add" ? "Adding" : "Removing").bright} alias #{ip.bright} #{action == "add" ? "to" : "from"} interface #{interface.bright}.")
      system("sudo ifconfig #{interface} #{action == "add" ? "alias" : "-alias"} #{ip}")
    end

    def run
      @logger = Akaer::Logger.create(@options_parser["log-file"], @options_parser["log-level"])

      command = (@options_parser.args[0] || "add").downcase
      aliases = []
      target_address = ""

      # Parse configuration file
      configuration = Akaer::Configuration.load(@options_parser["configuration"], @logger)

      # Now merge with the options
      @options_parser[].each_pair do |option, value|
        key = option.to_s.gsub("-", "_")
        configuration[key] = value if configuration.has_key?(key) && @options_parser.provided?(option)
      end

      # Instantiate the logger
      logger = Akaer::Logger.create(configuration.log_file, configuration.log_level)

      # Create address
      if !["add", "remove"].include?(command) then
        @logger.fatal("\"Please choose an action between \"add\" or \"remove\".")
        abort
      end

      begin
        if configuration.addresses.blank? then
          target_address = configuration.start_address
          temp = IPAddr.new(target_address)

          [configuration.aliases, 1].max.times do
            aliases << temp.to_s
            temp = temp.succ
            target_address = temp.to_s
          end
        else
          configuration.addresses.each do |t|
            target_address = t
            aliases << IPAddr.new(target_address)
          end
        end
      rescue ArgumentError => e
        logger.fatal("\"#{target_address}\" is not a valid IP address.")
        abort
      end

      if !@options_parser["dry-run"] then
        total = aliases.length
        rj = total.to_s.length
        aliases.each_with_index do |a, i|
          prefix = "[#{(i + 1).to_s.rjust(rj, "0")}/#{total}]"
          if !self.execute_command(configuration.interface, command, a, prefix) then
            logger.fatal("#{command == "add" ? "Adding" : "Removing"} alias #{a} failed.")
            abort
          end
        end
      else
        @logger.info("I will #{command.bright} #{aliases.length == 1 ? "this alias" : "these aliases"} #{command == "add" ? "to" : "from"} interface #{configuration.interface.bright}: #{aliases.collect {|a| a.bright}.join(", ")}.")
      end
    end
  end
end