require "fileutils"

require "log4r"

module VagrantPlugins
  module HyperV
    module Action
      class Import
        def initialize(app, env)
          @app = app
          @logger = Log4r::Logger.new("vagrant::hyperv::import")
        end

        def call(env)
          vm_dir = env[:machine].box.directory.join("Virtual Machines")
          hd_dir = env[:machine].box.directory.join("Virtual Hard Disks")
          memory = env[:machine].provider_config.memory
          maxmemory = env[:machine].provider_config.maxmemory
          cpus = env[:machine].provider_config.cpus
          vmname = env[:machine].provider_config.vmname
          differencing_disk = env[:machine].provider_config.differencing_disk
          auto_start_action = env[:machine].provider_config.auto_start_action
          auto_stop_action = env[:machine].provider_config.auto_stop_action
          enable_virtualization_extensions = env[:machine].provider_config.enable_virtualization_extensions
          vm_integration_services = env[:machine].provider_config.vm_integration_services

          env[:ui].output("Configured Dynamic memory allocation, maxmemory is #{maxmemory}") if maxmemory
          env[:ui].output("Configured startup memory is #{memory}") if memory
          env[:ui].output("Configured cpus number is #{cpus}") if cpus
          env[:ui].output("Configured enable virtualization extensions is #{enable_virtualization_extensions}") if enable_virtualization_extensions
          env[:ui].output("Configured vmname is #{vmname}") if vmname
          env[:ui].output("Configured differencing disk instead of cloning") if differencing_disk
          env[:ui].output("Configured automatic start action is #{auto_start_action}") if auto_start_action
          env[:ui].output("Configured automatic stop action is #{auto_stop_action}") if auto_stop_action

          if !vm_dir.directory? || !hd_dir.directory?
            raise Errors::BoxInvalid
          end

          config_path = nil
          config_type = nil
          vm_dir.each_child do |f|
            if f.extname.downcase == '.xml'
              @logger.debug("Found XML config...")
              config_path = f
              config_type = 'xml'
              break
            end
          end

          vmcx_support = env[:machine].provider.driver.execute("has_vmcx_support.ps1", {})['result']
          if vmcx_support
            vm_dir.each_child do |f|
              if f.extname.downcase == '.vmcx'
                @logger.debug("Found VMCX config and support...")
                config_path = f
                config_type = 'vmcx'
                break
              end
            end
          end

          image_path = nil
          image_ext = nil
          image_filename = nil
          hd_dir.each_child do |f|
            if %w{.vhd .vhdx}.include?(f.extname.downcase)
              image_path = f
              image_ext = f.extname.downcase
              image_filename = File.basename(f, image_ext)
              break
            end
          end

          if !config_path || !image_path
            raise Errors::BoxInvalid
          end

          env[:ui].output("Importing a Hyper-V instance")

          switches = env[:machine].provider.driver.execute("get_switches.ps1", {})
          raise Errors::NoSwitches if switches.empty?

          switch = nil
          env[:machine].config.vm.networks.each do |type, opts|
            next if type != :public_network && type != :private_network

            switchToFind = opts[:bridge]

            if switchToFind
              puts "Looking for switch with name: #{switchToFind}"
              switch = switches.find { |s| s["Name"].downcase == switchToFind.downcase }["Name"]
              puts "Found switch: #{switch}"
            end
          end

          if switch.nil?
            if switches.length > 1
              env[:ui].detail(I18n.t("vagrant_hyperv.choose_switch") + "\n ")
              switches.each_index do |i|
                switch = switches[i]
                env[:ui].detail("#{i+1}) #{switch["Name"]}")
              end
              env[:ui].detail(" ")

              switch = nil
              while !switch
                switch = env[:ui].ask("What switch would you like to use? ")
                next if !switch
                switch = switch.to_i - 1
                switch = nil if switch < 0 || switch >= switches.length
              end
              switch = switches[switch]["Name"]
            else
              switch = switches[0]["Name"]
            end
          end

          env[:ui].detail("Cloning virtual hard drive...")
          source_path = image_path.to_s
          dest_path = env[:machine].data_dir.join("Virtual Hard Disks").join("#{image_filename}#{image_ext}").to_s

          # Still hard copy the disk of old XML configurations
          if config_type == 'xml'
            if differencing_disk
              env[:machine].provider.driver.execute("clone_vhd.ps1", {Source: source_path, Destination: dest_path})
            else
              FileUtils.mkdir_p(env[:machine].data_dir.join("Virtual Hard Disks"))
              FileUtils.cp(source_path, dest_path)
            end
          end
          image_path = dest_path

          # We have to normalize the paths to be Windows paths since
          # we're executing PowerShell.
          options = {
              vm_config_file: config_path.to_s.gsub("/", "\\"),
              vm_config_type: config_type,
              source_path:    source_path.to_s,
              dest_path:      dest_path,
              data_path:      env[:machine].data_dir.to_s.gsub("/", "\\")
          }
          options[:switchname] = switch if switch
          options[:memory] = memory if memory
          options[:maxmemory] = maxmemory if maxmemory
          options[:cpus] = cpus if cpus
          options[:vmname] = vmname if vmname
          options[:auto_start_action] = auto_start_action if auto_start_action
          options[:auto_stop_action] = auto_stop_action if auto_stop_action
          options[:differencing_disk] = differencing_disk if differencing_disk
          options[:enable_virtualization_extensions] = "True" if enable_virtualization_extensions and enable_virtualization_extensions == true

          env[:ui].detail("Creating and registering the VM...")
          server = env[:machine].provider.driver.import(options)

          env[:ui].detail("Setting VM Integration Services")
          vm_integration_services.each do |key, value|
            state = false
            if value === true
              state = "enabled"
            elsif value === false
              state = "disabled"
            end
            env[:ui].output("#{key} is #{state}") if state
          end

          vm_integration_services[:VmId] = server["id"]
          env[:machine].provider.driver.set_vm_integration_services(vm_integration_services)

          env[:ui].detail("Successfully imported a VM with name: #{server['name']}")
          env[:machine].id = server["id"]
          @app.call(env)
        end
      end
    end
  end
end