ActionController::Base.class_eval do 
  def self.string_layout( the_string_layout, conditions = {} )
    if the_string_layout.nil?
      clear_string_layout_conditions
    else
      add_string_layout_conditions( conditions )
    end
    
    write_inheritable_attribute( :string_layout, the_string_layout )
  end


  def self.string_layout_conditions #:nodoc:
    read_inheritable_attribute( :string_layout_conditions )
  end
  
  def self.add_string_layout_conditions( conditions )
    write_inheritable_hash :string_layout_conditions, normalize_conditions( conditions )
  end

  def self.clear_string_layout_conditions
    write_inheritable_attribute :string_layout_conditions, { }
  end

  def default_string_layout
    self.class.read_inheritable_attribute( :string_layout )
  end

  def active_string_layout( passed_string_layout = nil )
    string_layout_blob = passed_string_layout || default_string_layout

    the_active_string_layout = case string_layout_blob
                               when String
                                 StringLayout.new( :layout => string_layout_blob )
                               when Symbol
                                 send( string_layout_blob )
                               when Proc
                                 string_layout_blob.call( self )
                               end
                               
    if the_active_string_layout.nil?
      nil
    elsif the_active_string_layout.respond_to?( :layout )
      the_active_string_layout
    elsif the_active_string_layout.respond_to?( :to_s )
      StringLayout.new( :layout => the_active_string_layout.to_s )
    else
      StringLayout.new( :layout => '<%= @content_for_layout %>' )
    end
  end

  def render_with_a_string_layout( options = nil, deprecated_status = nil, deprecated_layout = nil, &block )
    template_with_options = options.is_a?( Hash )

    return render_with_no_string_layout( options, deprecated_status, &block ) unless apply_string_layout?( template_with_options, options )
    the_string_layout = pick_string_layout( template_with_options, options, deprecated_layout )
    return render_with_no_string_layout( options, deprecated_status, &block ) unless the_string_layout.respond_to?( :layout )

    logger.info( "Rendering #{options} within string_layout #{the_string_layout}" ) if logger

    if template_with_options
      content_for_layout = render_with_no_string_layout( options, &block )
      deprecated_status = options[:status] || deprecated_status
    else
      content_for_layout = render_with_no_string_layout( options, deprecated_status, &block )
    end

    erase_render_results
    
    add_variables_to_assigns
    @template.instance_variable_set( "@content_for_layout", content_for_layout )

    if the_string_layout.respond_to?( :layout )
      render_text( @template.compile_and_render_template( nil, the_string_layout.layout ), deprecated_status )
    else
      render_text( @template.render_file( the_string_layout, true ), deprecated_status )
    end
  end

  def apply_string_layout?( template_with_options, options )
    return false if options == :update
    template_with_options ?  candidate_for_string_layout?( options ) : !template_exempt_from_layout?
  end

  def candidate_for_string_layout?( options )
    ( options.has_key?( :string_layout ) && options[:string_layout] != false ) || 
    !template_exempt_from_layout?( default_template_name( options[:action] || options[:template] ) )
  end


  def pick_string_layout( template_with_options, options, deprecated_layout )
    if deprecated_layout.respond_to?( :layout )
      deprecated_layout
    elsif template_with_options
      case string_layout = options[:string_layout]
        when FalseClass
          nil
        when NilClass, TrueClass
          active_string_layout if action_has_string_layout?
        else
          active_string_layout( string_layout )
      end
    else
      active_string_layout if action_has_string_layout?
    end
  end

  def action_has_string_layout?
    if conditions = self.class.string_layout_conditions
      case
        when only = conditions[:only]
          only.include?( action_name )
        when except = conditions[:except]
          !except.include?( action_name ) 
        else
          true
      end
    else
      true
    end
  end

  alias_method :render_with_no_string_layout, :render_with_a_layout
  alias_method :render, :render_with_a_string_layout
end