module Erector

  # Externals are a mechanism by which a widget can declare page-level
  # resources upon which it depends. They are not emitted during the widget's
  # normal emiting process. Rather, the Erector::Widget::Page keeps track of
  # all the widgets it emits, then goes back and inserts the proper tags for
  # all the externals inside its HEAD element.
  module Externals
    def self.included(base)
      base.extend ClassMethods
    end

    module ClassMethods

      # Express a dependency of this widget
      # Multiple forms:
      #   depends_on(type, text, options = {})
      # for example
      #   depends_on(:js, '/foo.js', :embed=>true)
      #
      # Other variants:
      #   depends_on(type, an_io, ... # file to be read
      #   depends_on('blah.js' ... infer :js
      #   depends_on('blah.css' ... infer :css
      #   depends on :js, 'file1.js', 'file2.js'... [options]
      #   depends_on :js => ["foo.js", "bar.js"], :css=>['file.css']
      #   depends_on :js => ["foo.js", "bar.js"], other_option=>:blah
      def depends_on(*args)
        x = interpret_args(*args)
        my_dependencies.push(x)
      end

      # deprecated in favor of #depends_on
      # todo: warning
      def external(type, value, options = {})
        type = type.to_sym
        x = Dependency.new(type, value, options)
        my_dependencies << x unless my_dependencies.include?(x)
      end

      # returns all dependencies of the given type from this class and all its
      # superclasses
      def dependencies(type)
        type = type.to_sym
        deps = Dependencies.new
        deps.push(*superclass.dependencies(type)) if superclass.respond_to?(:dependencies)
        deps.push(*my_dependencies.select { |x| x.type == type })
        deps.uniq
      end

      def my_dependencies
        @my_dependencies ||= Dependencies.new
      end

      private
      INFERABLE_TYPES = [:css, :js]

      def interpret_args(*args)
        options =  {}
        options = args.pop if args.last.is_a?(::Hash)
        if args.empty? && options.any?
          deps = []
          texts_hash = {}
          INFERABLE_TYPES.each do |t|
            texts_hash[t] = options.delete(t) if options.has_key? t
          end
          texts_hash.each do |t, texts|
            [texts].flatten.each do |text|
              deps << interpret_args(t, text, options)
            end
          end
          return deps
        elsif args[0].class == Symbol
          type = args.shift
        else
          type = /.+\.js/.match(args[0]) ? :js : :css
        end

        deps = args.map do |text|
          Dependency.new(type, text, options)
        end
        deps.size == 1 ? deps.first : deps
      end
    end

    def render_with_externals(options_to_external_emiter = {})
      output = Erector::Output.new
      self.to_a(:output => output) # emit all the externals onto this new output buffer
      nested_widgets = output.widgets.to_a
      options_to_external_emiter = {:classes => nested_widgets}.merge(options_to_external_emiter)
      emiter = ExternalRenderer.new(options_to_external_emiter)
      externals = emiter.to_a(:output => output)
      output.to_a
    end

    def render_externals(options_to_external_emiter = {})
      output_for_externals = Erector::Output.new
      nested_widgets = output.widgets
      externalizer = ExternalRenderer.new({:classes => nested_widgets}.merge(options_to_external_emiter))
      externalizer._emit(:output => output_for_externals)
      output_for_externals.to_a
    end
  end
end
