#!/usr/bin/ruby
$: << File.join(File.dirname(__FILE__), "..", "lib")
require 'rubygems'
require 'active_support/inflector'
require 'clamp'
require 'vmit'

#Vmit.logger = Logger.new(STDERR)
# Don't show by default anything
#Vmit.logger.level = Logger::INFO
#Vmit.logger.level = Logger::DEBUG if ENV['DEBUG']
# TODO find a good format
#Vmit.logger.formatter = proc do |severity, datetime, progname, msg|
#  "#{datetime}: #{severity} #{msg}\n"
#end

module Vmit

  class BaseVmCommand < Clamp::Command
    option ["-m", "--memory"], "N", "Amount of RAM" do |memory|
      if not memory =~ /(\d)+(M|K|G)/
        raise ArgumentError, "Memory should be given as 1M, 2G, etc"
      end
      memory
    end

    option ['-u', '--uuid'], "UUID", "unique id for the virtual machine"
  end

  class InitCommand < BaseVmCommand

    OPTION_KEYS = [:disk_size, :memory, :uuid]

    option ["-s","--disk-size"], "SIZE",
        "Initialize disk with SIZE (eg: 10M, 10G, 10K)" do |disk_size|
      if not disk_size =~ /(\d)+(M|K|G)/
        raise ArgumentError, "Disk size should be given as 1M, 2G, etc"
      end
      disk_size
    end

    def execute
      options = {}
      vm = Vmit::Workspace.new(File.expand_path(Dir.pwd))

      OPTION_KEYS.each do |k|
        val = self.send(k)
        options[k] = val if val
      end

      vm.config.configure(options)
      vm.save_config!

      vm.disk_image_init!(options)

    end

  end

  class UpCommand < BaseVmCommand

    OPTION_KEYS = [:memory, :cdrom]

    option ["-cdrom","--cdrom"], "CD", "Use CD as cdrom device (ISO, dev)" do |cdrom|
      if not File.exist?(cdrom)
        raise ArgumentError, "CDROM cdevice #{cdrom} not found"
      end
      cdrom
    end

    def execute
      options = {}
      workspace = Vmit::Workspace.new(File.expand_path(Dir.pwd))

      OPTION_KEYS.each do |k|
        val = self.send(k)
        options[k] = val if val
      end

      vm = Vmit::LibvirtVM.new(workspace, options)
      vm.up
    end

  end

  class SpiceCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.spice
    end
  end

  class VncCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.vnc
    end
  end

  class SshCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.assert_up

      while not vm.ip_address
        sleep(1)
        Vmit.logger.info("Waiting for ip address...")
      end
      Vmit.logger.info("ip address is #{vm.ip_address}")
      Vmit::Utils.wait_for_port(vm.ip_address, 22)
      system("ssh root@#{vm.ip_address}")
    end
  end

  class StatusCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      st, reason = vm.state

      puts "VM is #{st}...(#{reason})"
      if vm.ip_address
        puts "  #{vm.ip_address}"
      end

    end
  end

  class RebootCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.reboot
    end
  end

  class ShutdownCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.shutdown
    end
  end

  class DestroyCommand < Clamp::Command
    def execute
      vm = LibvirtVM.from_pwd
      vm.destroy
    end
  end

  class DiskSnapshotCommand < Clamp::Command

    def execute
      vm = LibvirtVM.from_pwd
      vm.assert_down

      vm.workspace.disk_snapshot!
    end

  end

  class DiskImagesCommand < Clamp::Command

    def execute
      vm = LibvirtVM.from_pwd
      puts vm.workspace.disk_images.last
    end

  end

  class DiskRollbackCommand < Clamp::Command

    def execute
      vm = LibvirtVM.from_pwd
      vm.assert_down

      vm.workspace.disk_rollback!
    end

  end

  Vmit.load_plugins!

  class MainCommand < Clamp::Command

    subcommand "init", "Initialize the vm", InitCommand
    subcommand "up", "Run the vm", UpCommand
    subcommand "status", "Show VM status", StatusCommand
    subcommand "shutdown", "Shutdown the VM", ShutdownCommand
    subcommand "reboot", "Reboot the VM", RebootCommand
    subcommand "destroy", "Destroy the VM", DestroyCommand
    subcommand "disk-images", "List disk images", DiskImagesCommand
    subcommand "disk-snapshot", "Creates a new disk-snapshot", DiskSnapshotCommand
    subcommand "disk-rollback", "Rollbacks to previous disk snapshot", DiskRollbackCommand
    subcommand "spice", "SPICE into the machine", SpiceCommand
    subcommand "ssh", "SSH into the machine", SshCommand
    subcommand "vnc", "VNC into the machine", VncCommand

    # Add commands offered via plugins
    Vmit.plugins.each do |plugin|
      if plugin.is_a?(Class) && plugin.ancestors.include?(::Clamp::Command)
        plugin_id = plugin.to_s.split('::').last.underscore
        subcommand plugin_id, "Plugin command", plugin
        Vmit.logger.debug "Enabling plugin #{plugin_id}"
      end
    end
  end

end

begin
  Vmit::MainCommand.run
rescue SystemExit, Interrupt
  Vmit.logger.fatal "interrupted"
  exit(1)
rescue Exception => e
  Vmit.logger.fatal e.message
  e.backtrace.each do |bt_line|
    Vmit.logger.debug "  #{bt_line}"
  end
  exit(1)
end
exit(0)