#!/usr/bin/env ruby
# Copyright (c) 2009, Keith Salisbury (www.globalkeith.com)
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
# 
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 
# Neither the name of the original author nor the names of contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

require 'yaml'
require 'erb'
require 'fileutils'
require 'subtrac/version'
require 'subtrac/commands'
require 'subtrac/client'
require 'subtrac/project'
require 'subtrac/apache'
require 'subtrac/svn'
require 'subtrac/trac'
require 'subtrac/core_ext'
require 'subtrac/config'

module Subtrac

  SUBTRAC_ROOT = "#{File.dirname(__FILE__)}/" unless defined?(SUBTRAC_ROOT)
  SUBTRAC_ENV = (ENV['SUBTRAC_ENV'] || 'development').dup unless defined?(SUBTRAC_ENV)
  
  # Loads the configuration YML file
  def self.load_config
    Config.load()
  end

  # Install
  def self.install(args,options)
    puts "\n==== Installing development server files ===="
    
    if options.defaults then
      overwrite = options.clean
      confirm_default_client = true
    else
      # check where we are installing
      Config.confirm_or_update(:install_dir,"install_dir")
      
      unless !File.directory?(install_dir) 
        # Ask if the user agrees (yes or no)
        confirm_clean = agree("Err, it seems there's some stuff in there. You sure you want me to overwrite? [Y/n]") if options.clean
        overwrite = agree("Doubly sure? I can't undo this....[Y/n]") if confirm_clean
      end

      # confirm server
      Config.confirm_or_update(:server_name,"server_name")

      # ask for hostname
      Config.confirm_or_update(:server_hostname,"server_hostname")

      # default client/project name
      Config.confirm_or_update(:default_client,"default_client")
      Config.confirm_or_update(:default_project,"default_project")

    end
    
    say("Ok we're about to install now, these are the options you have chosen:
    installation directory: #{Config.install_dir}
    overwrite: #{overwrite}
    server name: #{Config.server_name}
    server hostname: #{Config.server_hostname}
    default client: #{Config.default_client}
    default project: #{Config.default_project}")

    confirm = agree("Is this ok? [y/n]")  
    
    exit 0 if !confirm
    
    create_environment_directories(overwrite)

    #create a new virtual host
    apache = Apache.new
    apache.create_virtual_host()
    
    # create the trac site
    trac = Trac.new
    trac.install_common_files()
    
    install_common_files()
    configure_admin_user()
    
    # create default project
    create_project(Config.default_project,Config.default_client)

    # store any user preferences for later use
    Config.save()

  end
  
  def self.create_project(project,client)
    
    # create default project
    project = Project.new(project,client,Config.default_project_type)
    
    # get these out for binding...we'll tidy up later
    #client = project.client
    Config.project = project
    Config.client = project.client

    # create the svn repo
    svn = Svn.new
    svn.create_project(project)
    
    # create the trac site
    trac = Trac.new
    trac.create_project(project)
    
    # create the apache configuration
    apache = Apache.new
    apache.create_project(project)

    # fix privileges
    give_apache_privileges()
    
  end

  private
  
  def self.give_apache_privileges
    # make sure apache can operate on these files
    `sudo chown -R www-data:www-data #{Config.install_dir}`
  end
  
  def self.install_common_files    
    puts "\n==== Installing common files ===="
    # TODO: implement a mask for .svn folders
    # TODO: refactor /common to the app config
    FileUtils.cp_r(Dir.glob(File.join(Config.subtrac_path, "common/.")),Config.docs_dir)
  end
  
  def self.configure_admin_user
    puts "\n==== Configure admin user ===="
    # create admin user
    admin_user = ask("New admin user:  ") { |q| q.echo = true }
    admin_pass = ask("New password:  ") { |q| q.echo = "*" }
    admin_pass_confirm = ask("Re-type new password:  ") { |q| q.echo = "*" }
    if (admin_pass == admin_pass_confirm) then
      passwd_file = File.join(Config.install_dir, ".passwd")
      `htpasswd -c -b #{passwd_file} #{admin_user} #{admin_pass}`
      Config.data[:admin_user] = admin_user
      Config.data[:admin_pass] = admin_pass
      # ensure this guy is added to trac admin group
      Config.data[:trac][:permissions][admin_user] = "admins"
    else
      # call the password chooser again
      configure_admin_user()
    end
  end

  def self.create_environment_directories(overwrite=false)
    puts "\n==== Creating new environment directories ===="
    FileUtils.rm_rf Config.install_dir if overwrite
    File.create_if_missing Config.install_dir
    # create the environment directories
    Config.data[:dirs].each do |key, value|
      dir = File.join(Config.install_dir,value)
      File.create_if_missing dir
    end
  end

end