#!/usr/bin/env ruby
#
# Copyright (C) 2009  Red Hat, Inc.
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.  The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License.  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
# License for the specific language governing permissions and limitations
# under the License.

require 'rubygems'
require 'optparse'
require 'uri'
require 'lib/deltacloud'
require 'lib/plain_formatter'

include DeltaCloud::PlainFormatter

options = {
  :verbose => false
}

@optparse = OptionParser.new do |opts|

opts.banner = <<BANNER
Usage:
deltacloudc collection operation [options]

URL format:
API_URL=http://[user]:[password]@[api_url][port][/uri]

Options:
BANNER
  opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id }
  opts.on( '-d', '--image-id ID', 'Image ID') { |id| options[:image_id] = id }
  opts.on( '-a', '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id }
  opts.on( '-p', '--hardware-profile HARDWARE_PROFILE', 'Hardware Profile') { |id| options[:hwp_id] = id }
  opts.on( '-n', '--name NAME', 'Name (for instance eg.)') { |name| options[:name] = name }
  opts.on( '-s', '--state STATE', 'Instance state (RUNNING, STOPPED)') { |state| options[:state] = state }
  opts.on( '-u', '--url URL', 'API url ($API_URL variable)') { |url| options[:api_url] = url }
  opts.on( '-l', '--list', 'List collections/operations') { |id| options[:list] = true }
  opts.on( '-h', '--help', 'Display this screen' ) { puts opts ; exit }
  opts.on( '-v', '--version', 'Display API version' ) { options[:version]=true }
  opts.on( '-V', '--verbose', 'Print verbose messages' ) { options[:verbose]=true }
end

def invalid_usage(error_msg='')
  puts "ERROR: #{error_msg}"
  exit(1)
end

@optparse.parse!

# First try to get API_URL from environment
options[:api_url] = ENV['API_URL'] if options[:api_url].nil?

url = URI.parse(options[:api_url])
api_url = "http://#{url.host}#{url.port ? ":#{url.port}" : ''}#{url.path}"

options[:collection] = ARGV[0]
options[:operation] = ARGV[1]

# Connect to Deltacloud API and fetch all entry points
client = DeltaCloud.new(url.user || ENV['API_USER'], url.password || ENV['API_PASSWORD'], api_url)
collections = client.entry_points.keys

# Exclude collection which don't have methods in client library yet
collections.delete(:instance_states)

# If list parameter passed print out available collection
# with API documentation
if options[:list] and options[:collection].nil?
  collections.each do |c|
    doc = client.fetch_documentation(c.to_s)
    puts sprintf("%-22s: %s", c.to_s[0, 22], doc[:description])
  end
  exit(0)
end

# If collection parameter is present and user requested list
# print all operation defined for collection with API documentation
if options[:list] and options[:collection]
  doc = client.fetch_documentation(options[:collection])
  doc[:operations].each do |c|
    puts sprintf("%-20s: %s", c[:name][0, 20], c[:description])
  end
  exit(0)
end

if options[:version]
  puts "Deltacloud API(#{client.driver_name}) 0.1"
  exit(0)
end

# List items from collection (typically /instances)
# Do same if 'index' operation is set
if options[:collection] and ( options[:operation].nil? or options[:operation].eql?('index') )
  invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)
  params = {}
  params.merge!(:architecture => options[:architecture]) if options[:architecture]
  params.merge!(:id => options[:id]) if options[:id]
  params.merge!(:state => options[:state]) if options[:state]
  client.send(options[:collection].to_s, params).each do |model|
    puts format(model)
  end
  exit(0)
end

if options[:collection] and options[:operation]

  invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)

  params = {}
  params.merge!(:id => options[:id]) if options[:id]

  # If collection is set and requested operation is 'show' just 'singularize'
  # collection name and print item with specified id (-i parameter)
  if options[:operation].eql?('show')
    puts format(client.send(options[:collection].gsub(/s$/, ''), options[:id]))
    exit(0)
  end

  # If collection is set and requested operation is create new instance,
  # --image-id, --hardware-profile and --name parameters are used
  # Returns created instance in plain form
  if options[:collection].eql?('instances') and options[:operation].eql?('create')
    invalid_usage("Missing image-id") unless options[:image_id]
    if options[:name] and ! client.feature?(:instances, :user_name)
      invalid_usage("Driver does not support user-supplied name")
    end
    params.merge!(:name => options[:name]) if options[:name]
    params.merge!(:image_id => options[:image_id]) if options[:image_id]
    params.merge!(:hwp_id => options[:hwp_id]) if options[:hwp_id]
    instance = client.create_instance(options[:image_id], params)
    puts format(instance)
    exit(0)
  end

  # All other operations above collections is done there:
  if options[:collection].eql?('instances')
    instance = client.instance(options[:id])
    instance.send("#{options[:operation]}!".to_s)
    instance = client.instance(options[:id])
    puts format(instance)
    exit(0)
  end
end

# If all above passed (eg. no parameters)
puts @optparse