require "log4r"

module VagrantPlugins
  module ProviderVirtualBox
    module Action
      class SaneDefaults
        def initialize(app, env)
          @logger = Log4r::Logger.new("vagrant::action::vm::sanedefaults")
          @app = app
        end

        def call(env)
          # Set the env on an instance variable so we can access it in
          # helpers.
          @env = env

          # Enable the host IO cache on the sata controller. Note that
          # if this fails then its not a big deal, so we don't raise any
          # errors. The Host IO cache vastly improves disk IO performance
          # for VMs.
          command = [
            "storagectl", env[:machine].id,
            "--name", "SATA Controller",
            "--hostiocache", "on"
          ]
          attempt_and_log(command, "Enabling the Host I/O cache on the SATA controller...")

          # Use rtcuseutc so that the VM sees UTC time.
          command = ["modifyvm", env[:machine].id, "--rtcuseutc", "on"]
          attempt_and_log(command, "Enabling rtcuseutc...")

          if env[:machine].provider_config.auto_nat_dns_proxy
            @logger.info("Automatically figuring out whether to enable/disable NAT DNS proxy...")

            # Enable/disable the NAT DNS proxy as necessary
            if enable_dns_proxy?
              command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "on"]
              attempt_and_log(command, "Enable the NAT DNS proxy on adapter 1...")
            else
              command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "off" ]
              attempt_and_log(command, "Disable the NAT DNS proxy on adapter 1...")
              command = ["modifyvm", env[:machine].id, "--natdnshostresolver1", "off" ]
              attempt_and_log(command, "Disable the NAT DNS resolver on adapter 1...")
            end
          else
            @logger.info("NOT trying to automatically manage NAT DNS proxy.")
          end

          @app.call(env)
        end

        protected

        # This is just a helper method that executes a single command, logs
        # the given string to the log, and also includes the exit status in
        # the log message.
        #
        # We assume every command is idempotent and pass along the `retryable`
        # flag. This is because VBoxManage is janky about running simultaneously
        # on the same box, and if we up multiple boxes at the same time, a bunch
        # of modifyvm commands get fired
        #
        # @param [Array] command Command to run
        # @param [String] log Log message to write.
        def attempt_and_log(command, log)
          begin
            @env[:machine].provider.driver.execute_command(
              command + [retryable: true])
          rescue Vagrant::Errors::VBoxManageError => e
            @logger.info("#{log} (error = #{e.inspect})")
          end
        end

        # This uses some heuristics to determine if the NAT DNS proxy should
        # be enabled or disabled. See the comments within the function body
        # itself to see the checks it does.
        #
        # @return [Boolean]
        def enable_dns_proxy?
          begin
            contents = File.read("/etc/resolv.conf")

            if contents =~ /^nameserver 127\.0\.(0|1)\.1$/
              # The use of both natdnsproxy and natdnshostresolver break on
              # Ubuntu 12.04 and 12.10 that uses resolvconf with localhost. When used
              # VirtualBox will give the client dns server 10.0.2.3, while
              # not binding to that address itself. Therefore disable this
              # feature if host uses the resolvconf server 127.0.0.1 or
              # 127.0.1.1
              @logger.info("Disabling DNS proxy since resolv.conf contains 127.0.0.1 or 127.0.1.1")
              return false
            end
          rescue Errno::ENOENT; end

          return true
        end
      end
    end
  end
end
