module Sprout

##
# Sprouts provides us with the ability to distribute source files, 
# precompiled libraries, and remote executables. It does all of 
# this by (ab)using RubyGems.
#
# RubyGems gives us the ability to version-manage, and distribute 
# arbitrary text and binary payloads.
#
# In order to support Flash development, we have one major problem
# that RubyGems does not solve for us. This is the fact that 
# many elements of our executable chain are not open-source, and we do
# not have the rights to directly distribute them.
#
# This restriction means that many of our tools and dependencies cannot be 
# packaged and distributed _directly_ within a RubyGem (or from
# any server other than Adobe's as a matter of fact).
#
# In order to overcome this restriction, we have introduced
# a Sprout::Specification. This is a regular Ruby file with regular
# Ruby code in it. The main idea behind this file, is that it
# needs to be given a name and available in your load path.
#
# Whenever a rake build task (Sprout::Executable) or library task,
# (Sprout::Library) is encountered, it will call
# Sprout::Executable.load or Sprout::Library.load (respectively).
#
# These methods will attempt to +require+ the provided
# specification and - if it's in your load path - the specification
# will be loaded, and any relevant file targets will be returned.
#
# There are many ways to get Ruby code into your load path.
# One of the easiest to package it up in a RubyGem and
# configure the +require_paths+ parameter of  your Gem::Specification.
#
# http://docs.rubygems.org/read/chapter/20#require_paths
#
# To learn more about packaging RubyGems:
#
# http://docs.rubygems.org/read/chapter/20#page85
# http://rubygems.rubyforge.org/rdoc/Gem/Specification.html
#
# To learn more about published RubyGems:
# 
# http://rubygems.org/pages/gem_docs
#
# To package a SWC library into a Sprout RubyGem, you would create a file (usually)
# named [project_name.spec] in the root of the project.
#
# This is your Gem::Specification.
#
# You would also create a file named [projet_name.rb] and put that
# into the root of the project or some other folder that you have added to 
# the Gem::Specification.require_paths parameter.
#
# == Example: Include a file directly in the RubyGem
#
# In the case of AsUnit, this file would be named asunit4.rb and it's contents
# are as follows:
#
#    :include:../../test/fixtures/specification/asunit4.rb
#
# == Example: Refer to files that are not in the RubyGem
#
# For projects like the Flex SDK, we can't distribute many of the required files,
# so we can refer to these files in our Sprout::Specification as +remote_file_targets+.
#
#    :include:../../test/fixtures/specification/flex4sdk.rb
#
# == Example: Create custom downloads for each supported platform
#
# For projects like the Flash Player itself, we need to refer to different 
# downloadable content for each supported platform.
#
#    :include:../../test/fixtures/specification/flashplayer.rb
#
# == Packaging and Sharing
#
# Public RubyGems are hosted at http://rubygems.org. 
class Specification

  attr_accessor :name
  attr_accessor :version

  attr_reader :file_targets
  attr_reader :load_path

  ##
  # Create a new Sprout::Specification.
  #
  # This method will yield the new Sprout::Specification to the provided block,
  # and delegate most configuration parameters to a {Gem::Specification}[http://rubygems.rubyforge.org/rdoc/Gem/Specification.html].
  #
  # To learn more about what parameters are available and/or required, please
  # check out RubyGems documentation for their {Gem::Specification}[http://rubygems.rubyforge.org/rdoc/Gem/Specification.html].
  #
  def initialize
    filename   = Sprout.file_from_caller caller.first
    @load_path = File.dirname filename
    @name      = File.basename(filename).gsub('.rb', '')
    yield self if block_given?
  end

  ##
  # Add a remote file target to this RubyGem so that when it
  # is loaded, Sprouts will go fetch this file from the network.
  #
  # Each time this method is called, a new Sprout::RemoteFiletarget instance will be yielded to
  # the provided block and resolved after the block completes.
  #
  # After this block is evaluated, Sprouts will first check the collection
  # of env_names to see if the expected paths are available. If a valid
  # env_name is found, Sprouts will return the path to the requested
  # executable from the environment variable.
  #
  # If no env_names are set, or the requested executable is not found within
  # any that are identified, Sprouts will check to see if the archive
  # has already been unpacked into the expected location:
  # 
  #     #{SPROUT_HOME}/cache/#{SPROUT_VERSION}/flex4sdk/#{md5}/4.0.pre
  #
  # If the archive been unpacked, Sprouts will return the path to the 
  # requested executable.
  #
  # If the archive has not been unpacked, Sprouts will check to see if the
  # archive has been downloaded to:
  #
  #     #{SPROUT_HOME}/cache/#{SPROUT_VERSION}/flex4sdk/#{md5}.zip
  #
  # If the archive has been downloaded, it will be unpacked and the path
  # to the requested executable will be returned.
  #
  # If the archive has not been downloaded, it will be downloaded, unpacked
  # and the path to the requested executable will be returned.
  def add_remote_file_target &block
    target = RemoteFileTarget.new
    configure_target target, &block
  end

  # Add a file to the RubyGem itself. This is a great way to package smallish libraries in either
  # source or already-packaged form. For example, one might add a SWC to a RubyGem library.
  # 
  # Each time this method is called, a new Sprout::FileTarget instance will be yielded to the
  # provided block, and added to a collection for packaging.
  #
  #     Sprout::Specification.new do |s|
  #        ...
  #        s.add_file_target do |t|
  #           t.platform = :universal
  #           t.add_executable :asdoc, 'bin/asdoc'
  #        end
  #     end
  #
  def add_file_target &block
    target = FileTarget.new
    configure_target target, &block
  end

  private

  def configure_target t, &block
    t.load_path   = load_path
    t.pkg_name    = name
    t.pkg_version = version
    yield t if block_given?
    register_file_target_libs_and_exes t
  end

  def register_file_target_libs_and_exes target
    register_items target.executables, Sprout::Executable, target
    # Reversing the libraries makes it so that definitions like:
    #
    #   target.add_library :swc, 'abcd'
    #   target.add_library :src, 'efgh'
    # 
    # When loading, if no name is specified, the :swc will be
    # returned to clients.
    register_items target.libraries, Sprout::Library, target
  end

  def register_items collection, ruby_feature, target
    collection.each do |exe_or_lib|
      exe_or_lib.pkg_name    = target.pkg_name
      exe_or_lib.pkg_version = target.pkg_version
      exe_or_lib.platform    = target.platform
      exe_or_lib.file_target = target
      ruby_feature.register exe_or_lib
    end
  end

end

end