#          Copyright (c) 2008 Michael Fellinger m.fellinger@gmail.com
# All files in this distribution are subject to the terms of the Ruby license.

require 'fileutils'
require 'find'

class ProjectCreator
  PROTO = []
  PROTO << '~/.proto/' if ENV["HOME"] # Guard against Windows
  attr_accessor :name, :options

  def initialize(name, options = {})
    @name, @options = name, options
  end

  def target
    File.expand_path(name)
  end

  def proto
    PROTO.map!{|pr| File.expand_path(pr) }
    proto = options[:proto] ||= PROTO.find{|f| File.directory?(f) }
    layout = options[:layout] ||= '/'
    File.expand_path(File.join(proto, layout))
  end

  def create_root?
    return true unless File.directory?(target)
    return true if amend? or force?
    fatal "%p is a directory, choose different project name or use --amend/--force" % target
  end

  def got_proto?
    return true if File.directory?(proto)
    fatal "Cannot create, %p doesn't exist, use --proto or create the proto directory" % proto
  end

  def create
    got_proto?

    puts "Found proto at: %p, proceeding...\n\n" % proto
    mkdir(relate('/')) if create_root?
    proceed
  end

  def proceed
    files, directories = partition{|path| File.file?(path) }
    proceed_directories(directories)
    proceed_files(files)
  end

  def proceed_files(files)
    files.each{|file| copy(file, relate(file)) }
  end

  def proceed_directories(dirs)
    dirs.each{|dir| mkdir(relate(dir)) }
  end

  def mkdir(dir)
    exists = File.directory?(dir)
    return if exists and amend?
    return if exists and not force?
    puts "mkdir(%p)" % dir
    FileUtils.mkdir_p(dir)
  end

  def copy(from, to)
    return unless copy_check(to)
    puts "copy(%p, %p)" % [from, to]
    FileUtils.cp(from, to, :preserve => true)
    post_process(to)
  end

  def copy_check(to)
    exists = File.file?(to)
    return if exists and amend?
    return if exists and not force?
    return true
  end

  # Think about a useful way to process the generated files it should be
  # possible to substitute some things like the project name in the
  # configuration

  def post_process(file)
    return
    source = File.read(file)
    File.open(file, 'w+') do |io|
      io.write(source.gsub('$project', "'#@name'"))
    end
  end

  def relate(path)
    File.join(target, path.to_s.sub(proto, ''))
  end

  def amend?; options[:amend] end
  def force?; options[:force] end

  def fatal(message)
    warn message
    exit 1
  end

  def each
    Dir["#{proto}/**/*"].each{|path| yield(path) }
  end

  include Enumerable
end
