#!/usr/bin/env ruby

# Copyright 2008-2011 Red Hat, Inc, and individual contributors.
# 
# This is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of
# the License, or (at your option) any later version.
# 
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this software; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.

require 'rubygems'

require 'thor'
require 'torquebox-rake-support'
require 'torquebox/server'
require 'torquebox/rails'

class TorqueBoxCommand < Thor

  map "run" => "start"
  desc "run", "Run TorqueBox (binds to localhost, use -b to override)"
  method_option :clustered, :type => :boolean, :desc => "Run TorqueBox in clustered mode"
  method_option 'data-directory', :type => :string, :desc => 'Override the directory TorqueBox uses to store it runtime data'
  method_option 'extra', :aliases => '-e', :type => :string, :desc => 'Extra options to pass through to JBoss AS, you will need to escape dashes with \ (e.g. \--help)'
  method_option 'max-threads', :type => :numeric, :desc => "Maximum number of HTTP threads"
  method_option 'bind-address', :aliases => '-b', :type => :string, :desc => "IP address to bind to - don't set this to 0.0.0.0 if used with --clustered"
  method_option 'port', :aliases => '-p', :type => :numeric, :desc => 'HTTP port to listen on'
  method_option 'node-name', :type => :string, :desc => 'Override the name of the node (which by default is the hostname)'
  method_option 'port-offset', :type => :numeric, :desc => 'Offset all port numbers listened on by TorqueBox by this number'
  method_option 'jvm-options', :aliases => '-J', :type => :string, :desc => 'Pass options on to the JVM'
  def start
    TorqueBox::Server.setup_environment
    TorqueBox::DeployUtils.run_server(:clustered => options.clustered,
                                      :max_threads => options['max-threads'],
                                      :bind_address => options['bind-address'],
                                      :port => options['port'],
                                      :port_offset => options['port-offset'],
                                      :pass_through => options['extra'],
                                      :node_name => options['node-name'],
                                      :data_directory => options['data-directory'],
                                      :jvm_options => options['jvm-options'])
  end

  desc "rails ROOT", "Create a Rails application at ROOT using the TorqueBox Rails template, or apply the TorqueBox template to an existing application at ROOT."
  def rails(root = ".", *args)
    ARGV.shift
    # Ensure the root defaults to the current directory if the user
    # doesn't specify one but passes in additional args
    if root.start_with?('-')
      args.unshift( root )
      root = '.'
    end
    TorqueBox::Server.setup_environment
    if File.exist?( File.join(root, 'config', 'environment.rb') )
      TorqueBox::Rails.apply_template( root )
    else
      TorqueBox::Rails.new_app( root )
    end
  end

  desc "archive ROOT", "Create a nice self-contained application archive"
  method_option :deploy, :type => :boolean, :desc => "Deploy the archive to TORQUEBOX_HOME."
  method_option :package_gems, :type => :boolean, :desc => "Include all Bundler gem dependencies in the archive."
  method_option :package_without, :type => :array, :desc => "Package without these bundler groups.", :default => "development test assets"
  method_option :precompile_assets, :type => :boolean, :desc => "Precompile all assets (Rails-specific)."
  long_desc <<-EOS
    Creates an application archive containing all of your application dependencies.
    The archive can be deployed to TorqueBox with the --deploy option or by
    hand after the archive file, known as a .knob, has been created by using
    `torquebox deploy myapp.knob`.
  EOS
  def archive(root = Dir.pwd)
    root = Dir.pwd if (root == '.')
    TorqueBox::Server.setup_environment
    archive_name = TorqueBox::DeployUtils.archive_name( root )
    path = TorqueBox::DeployUtils.create_archive(
      :name => archive_name,                            :app_dir => root,
      :precompile_assets => options.precompile_assets,  :package_gems => options.package_gems,
      :package_without => options.package_without
    )
    puts "Created archive: #{path}"
    if options.deploy
      puts "Deployed: #{archive_name}"
      puts "    into: #{ENV['TORQUEBOX_HOME']}"
      TorqueBox::DeployUtils.deploy_archive( :archive_path => path )
    end
  end

  desc "deploy ROOT", "Deploy an application to TorqueBox"
  long_desc <<-EOS
    Deploy an application to TorqueBox. The ROOT argument should point to either
    a directory containing the application you want to deploy, a -knob.yml file,
    a .knob archive, or any Java deployable artifact (.war, .ear, etc).
  EOS
  method_option :context_path, :type => :string, :desc => "Context Path (ex: /, /my_app)"
  method_option :env, :type => :string, :desc => "Application Environment (ex: development, test, production)"
  method_option :name, :type => :string, :desc => "The desired name of the deployment artifact (ex: foo)"
  def deploy(root = ".")
    TorqueBox::Server.setup_environment
    root = File.expand_path(root)
    opts = {:root => root}.merge(options)
    descriptor = TorqueBox::DeployUtils.basic_deployment_descriptor(opts)
    deployed_name, deploy_dir = TorqueBox::DeployUtils.deploy_yaml(descriptor, opts)
    failed_file = File.join( deploy_dir, "#{deployed_name}.failed" )
    if File.exists? failed_file
      puts "Removing failed descriptor: #{failed_file}" 
      FileUtils.rm( failed_file ) 
    end
    puts "Deployed: #{deployed_name}"
    puts "    into: #{deploy_dir}"
  end

  desc "undeploy ROOT", "Undeploy an application from TorqueBox"
  method_option :name, :type => :string, :desc => "The name of the artifact to undeploy (ex: foo)"
  def undeploy(root = ".")
    TorqueBox::Server.setup_environment
    root = File.expand_path(root)
    opts = {:root => root}.merge(options)
    deploy_name, deploy_dir = TorqueBox::DeployUtils.undeploy_yaml(opts)
    unless deploy_name
      deploy_name, deploy_dir = TorqueBox::DeployUtils.undeploy_archive(opts)
    end

    if deploy_name
      puts "Undeployed: #{deploy_name}"
      puts "      from: #{deploy_dir}"
    else
      puts "Nothing to undeploy"
    end
  end

  desc "list", "List applications deployed to TorqueBox and their deployment status"
  def list(root = ".")
    TorqueBox::Server.setup_environment
    apps = TorqueBox::DeployUtils.deployment_status
    puts "Nothing deployed." if apps.empty?
    apps.each do |k,v|
      puts k
      puts "  Descriptor: #{v[:descriptor]}"
      puts "  Status: #{v[:status]}"
    end
  end

  desc "cli", "Run the JBoss AS7 CLI"
  def cli
    TorqueBox::Server.setup_environment
    options = "--connect"
    if RbConfig::CONFIG['host_os'] =~ /mswin/
      path = File.join(ENV['JBOSS_HOME'], "bin\\jboss-cli")
      exec(path, options)
    else
      path = "/bin/sh #{File.join(ENV['JBOSS_HOME'], 'bin/jboss-cli.sh')}"
      exec "#{path} #{options}"
    end
  end

  desc "env [VARIABLE]", "Display TorqueBox environment variables"
  def env(variable=nil)
    TorqueBox::Server.setup_environment
    env_keys = %w(TORQUEBOX_HOME JBOSS_HOME JRUBY_HOME)
    if variable.nil?
      env_keys.each { |key| shell.say "#{key}=#{ENV[key]}" }
    else
      key = env_keys.find { |key| variable.downcase == key.downcase }
      shell.say(ENV[key]) unless key.nil?
    end
  end

end

TorqueBoxCommand.start
