#!/usr/bin/env ruby

require 'ascii_binder/engine'
require 'ascii_binder/helpers'
require 'ascii_binder/version'
require 'pathname'
require 'trollop'

include AsciiBinder::Engine
include AsciiBinder::Helpers

def call_generate(branch_group, distro, page)
  if page == ''
    page = nil
  end
  begin
    generate_docs(branch_group, distro, page)
  rescue => e
    message = "#{e.class.name}: #{e.message} at\n    #{e.backtrace.join("\n    ")}"
    Trollop::die "Could not generate docs:\n#{message}"
  end
end

def repo_check(docs_basedir)
  missing_files = false
  # These must all be present
  ['_distro_map.yml','_templates'].each do |file|
    unless File.exist?(File.join(docs_basedir, file))
      missing_files = true
    end
  end
  # Either of these must be present
  unless File.exist?(File.join(docs_basedir, '_build_cfg.yml')) or File.exist?(File.join(docs_basedir, '_topic_map.yml'))
    missing_files = true
  end
  if missing_files or not in_git_repo(docs_basedir)
    Trollop::die "The specified docs base directory '#{docs_basedir}' does not appear to be part of an AsciiBinder-compatible repo."
  end
end

def in_git_repo(dir)
  git_path = File.join(dir,'.git')
  return true if File.exist?(git_path) and File.directory?(git_path)
  return false if dir == '/'
  in_git_repo(File.expand_path('..',dir))
end

SUB_COMMANDS = %w{help version build watch package clean create clone}
Trollop::options do
  version AsciiBinder::VERSION
  banner <<-EOF
Usage:
  #$0 <command> <docs_basedir>

Commands:
  build (default action)
    Builds the HTML docs within the indicated docs base directory
  create
    Generates a new AsciiBinder repo at the indicated dir
  clone
    Clones an existing AsciiBinder repo to the local filesystem
  watch
    Starts Guard, which automatically regenerates changed HTML
    files on the working branch in the docs base directory dir
  package
    Builds and packages the static HTML for all of the sites
    defined in the _distro_config.yml file
  clean
    Remove _preview, _publish and _package dirs created by
    other AsciiBinder operations.

Options:
EOF
  stop_on SUB_COMMANDS
end

cmd = ARGV.shift
docs_basedir = nil

if cmd.nil?
  cmd = "build"
elsif !SUB_COMMANDS.include?(cmd)
  if ARGV.empty?
    docs_basedir = Pathname.new(cmd)
    cmd = "build"
  else
    Trollop::die "'#{cmd}' is not a valid asciibinder subcommand. Legal values are '#{SUB_COMMANDS.join('\', \'')}'."
  end
end

cmd_opts = case cmd
  when "build"
    Trollop::options do
      banner <<-EOF
Usage:
  #$0 build <options> <docs_basedir>

Description:
  This is the default behavior for the asciibinder utility. When run,
  AsciiBinder reads the _distro_config.yml file out of the working
  branch of the indicated docs base directory and based on that, proceeds
  to build the working branch version of the documentation for each distro.

  If you use the --all_branches flag, AsciiBinder behaves as described
  above, and then once the working branch version is built, AsciiBinder
  cycles through the other branches named in the _distro_config.yml file
  until all of the permutations have been built.

  If you want to limit the scope of the build work for faster builds,
  you have two targeted options:

  --distro=<distro_key> - Only builds the specified distro and branches
  associated with this distro.

  --page=<page_path> - Only builds the specified page for all distros.

  Note that the format for the "--page" option is:

  <topic_group>:<topic_file>

  or for subtopics:

  <topic_group>/<subtopic_group>:<topic_file>

  However, if you want to use the --page option extensively, then be
  aware of the `asciibinder watch` function, which does this for you
  automatically as you change any .adoc files in your working branch.

Options:
EOF
      opt :all_branches, "Instead of building only the current working branch, build all branches", :default => false
      opt :distro, "Instead of building all distros, build branches only for the specified distro.", :default => ''
      opt :page, "Build only the specified page for all distros and only the current working branch.", :default => ''
      conflicts :distro, :page
    end
  when "create"
    Trollop::options do
      banner <<-EOF
Usage:
  #$0 create <new_docs_basedir>

Description:
  Creates a new, bare AsciiBinder repo in the specified directory.
EOF
    end
  when "clone"
    Trollop::options do
      banner <<-EOF
Usage:
  #$0 clone <options> <git_repo_url>

Description:
  Clones an existing AsciiBinder repo to the current directory.
  Under the default behavior, AsciiBinder will attempt to set up
  tracking branches based on the contents of _distro_map.yml,
  but this can be suppressed (see Options).

Options:
EOF
      opt :branches, "Create tracking branches after cloning.", :default => true
      opt :dir, "Specify the pathname of the local directory for cloning.", :default => ''
    end
  when "watch"
    Trollop::options do
      banner <<-EOF
Usage:
  #$0 watch <docs_basedir>

Description:
  In watch mode, AsciiBinder starts a Guard process in the foreground.
  This process watches the docs_basedir for changes to the AsciiDoc (.adoc)
  files. When a change occurs, AsciiBinder regenerates the specific
  HTML output of the file that was changed, for the working branch only.

  This is the equivalent of running:

  $ asciibinder build --page='<topic_group>:<affected_file>'

  ...except that the Guardfile automatically detects and runs this as
  you work.

  This is meant to be used in conjunction with a web browser that is
  running a LiveReload plugin. If you are viewing the output HTML page
  in a browser where LiveReload is active, then every time you save a
  new version of the .adoc file, the new HTML is automatically
  regenerated and your page view is automatically refreshed.
EOF
    end
  when "package"
    Trollop::options do
      banner <<-EOF
Usage:
  #$0 package <options> <docs_basedir>

Description:
  Publish mode is similar to 'build' mode, but once all of the branches' of
  HTML are generated, 'publish' goes on to organize the branch / distro
  combinations that are described in _distro_config.yml into their "site"
  layouts. As a final step, the site layouts are tarred and gzipped for
  easy placement onto a production web server.

Options:
EOF
      opt :site, "Instead of packaging every docs site, package the specified site only.", :default => ''
    end
  when "help"
    Trollop::educate
  when "version"
    puts AsciiBinder::VERSION
    exit 0
  end

if (not docs_basedir.nil? and not ARGV.empty?) or (docs_basedir.nil? and ARGV.length > 1)
  Trollop::die "Too many arguments provided to ascii_binder: '#{ARGV.join(' ')}'. Exiting."
elsif docs_basedir.nil?
  if ARGV.length == 1
    if cmd == 'clone'
      cmd_opts[:giturl] = ARGV.shift
      if cmd_opts[:dir] != ''
        docs_basedir = Pathname.new(cmd_opts[:dir])
      else
        docs_basedir = Pathname.new(File.join(Pathname.pwd, cmd_opts[:giturl].split('/')[-1].split('.')[0]))
      end
    else
      docs_basedir = Pathname.new(ARGV.shift)
    end
  else
    if cmd != 'create'
      if cmd == 'clone'
        Trollop::die "Provide a git URL to clone from."
      else
        docs_basedir = Pathname.pwd
      end
    else
      Trollop::die "Specify a name for the new repo directory."
    end
  end
end

# Validate the docs_basedir path
if cmd == 'create' or cmd == 'clone'
  if docs_basedir.exist?
    Trollop::die "The specified new repo directory '#{docs_basedir}' already exists."
  end
else
  if !docs_basedir.exist?
    Trollop::die "The specified docs directory '#{docs_basedir}' does not exist."
  elsif !docs_basedir.directory?
    Trollop::die "The specified docs directory path '#{docs_basedir}' is not a directory."
  elsif !docs_basedir.readable?
    Trollop::die "The specified docs directory '#{docs_basedir}' is not readable."
  elsif !docs_basedir.writable?
    Trollop::die "The specified docs directory '#{docs_basedir}' cannot be written to."
  else
    repo_check(docs_basedir)
  end
end

# Set the repo root
set_docs_root_dir(File.expand_path(docs_basedir))

# Cloning? Time to try it.
if cmd == 'clone'
  puts "Cloning #{cmd_opts[:giturl]} to #{docs_basedir}"
  system("git clone #{cmd_opts[:giturl]} #{docs_basedir}")
  Trollop::die "The git URL could not be cloned: #{err}" if $?.exitstatus != 0

  # Make sure this cloned repo is legit.
  repo_check(docs_basedir)

  if cmd_opts[:branches]
    Dir.chdir(docs_basedir)
    puts "Tracking branch setup:"
    distro_branches.each do |doc_branch|
      next if doc_branch == 'master'
      puts "- #{doc_branch}"
      system("git branch #{doc_branch} origin/#{doc_branch}")
    end
  else
    puts "- Skipping tracking branch setup"
  end

  # Done and done.
  puts "Cloning complete."
  exit
end

# Change to the repo dir. This is necessary in order for
# AsciiDoctor to work properly.
if cmd != 'create'
  Dir.chdir docs_basedir
end

# Do the things with the stuff
case cmd
when "build"
  branch_group = cmd_opts[:all_branches] ? :all : :working_only
  build_distro = cmd_opts[:distro] || ''
  refresh_page = cmd_opts[:page] || nil
  call_generate(branch_group,build_distro,refresh_page)
when "package"
  clean_up
  package_site = cmd_opts[:site] || ''
  branch_group = package_site == '' ? :publish : "publish_#{package_site}".to_sym
  call_generate(branch_group,'',nil)
  package_docs(package_site)
when "watch"
  if !dir_empty?(preview_dir)
    guardfile_path = File.join(Gem::Specification.find_by_name("ascii_binder").full_gem_path, 'Guardfile')
    exec("guard -G #{guardfile_path}")
  else
    Trollop::die "Run 'asciibinder build' at least once before running 'asciibinder watch'."
  end
when "clean"
  clean_up
  puts "Cleaned up #{docs_basedir}."
when "create"
  create_new_repo
  puts "Created new repo in #{docs_basedir}."
end

exit
