# = builderobject.rb
#
# == Copyright (c) 2006 Thomas Sawyer
#
#   Ruby License
#
#   This module is free software. You may use, modify, and/or redistribute this
#   software under the same terms as Ruby.
#
#   This program is distributed in the hope that it will be useful, but WITHOUT
#   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#   FOR A PARTICULAR PURPOSE.
#
# == Authors and Contributors
#
# * Thomas Sawyer

# Author::    Thomas Sawyer
# Copyright:: Copyright (c) 2006 Thomas Sawyer
# License::   Ruby License

require 'facets/more/functor'
require 'facets/core/kernel/object_class'

# = BuilderObject
#
# Build content programatically with Ruby and Ruby's blocks.
#
# Builders can use either an implict or explicit receiver.
# Explicit is the default. To use implicit pass the :implicit
# option to the constructor.
#
# Implict building is more elegant in form, but it is not as
# functional because it makes it more difficult to refer to
# external references.
#
# BuilderObject avoides method name clashes by using
# Functor redirection. Unlike other implementations of
# the Builder patterns which append '!' to builder methods
# or simply use odd names to avoid clashes, BuilderObject
# routes all builder method vis the #out method.

class BuilderObject

  private *public_instance_methods

  def self.builder
    @builder ||= Class.new
  end

  def self.builder_include( mod )
    builder.class_eval { include mod }
  end

  def initialize(explicit=true)
    @explicit = explicit
    @stack = []
    @buffer = ''
  end

  #def builder
  #  @builder ||= object_class.builder.new
  #end

  def out( str=nil )
    if str
      @buffer << str
    else
      @builder_functor ||= Functor.new( &send(:method,:+) )
    end
  end

  def +( op, *args, &blk )
    @builder ||= object_class.builder.new
    @buffer << @builder.send( op, *args, &blk )
  end

  def to_s
    @buffer
  end

  def method_missing( op, *a, &b )
    if b
      @stack << @buffer
      @buffer = ''
      @explict ? b.call : instance_eval(&b)
      r = @buffer
      @buffer = @stack.pop
      @buffer << builder_call(op, r, *a, &b)
    else
      @buffer << builder_call(op, *a, &b)
    end
  end

end


class BuildingBlocks

  def initialize(mod, constructor)
    @module = mod
    @constructor = constructor
    @instance_eval = method(:instance_eval)

    @method = {}

    meths = []
    meths.concat singleton_methods
    meths.concat public_methods
    meths.concat protected_methods
    meths.concat private_methods
    meths.each do |m|
      @method[m.to_sym] = method(m)
    end

    class << self
      escape = []
      escape << 'p'
      escape << 'singleton_method_undefined'
      escape << 'method_missing'
      escape << 'initialize'
      escape << 'inspect'
      escape << 'to_str'
      escape << 'to_s'
      #escape << 'to_ary'
      escape << 'respond_to?'
      meths = []
      meths.concat singleton_methods
      meths.concat public_instance_methods
      meths.concat protected_instance_methods
      meths.concat private_instance_methods
      meths.each do |m|
        undef_method(m) unless m =~ /^__/ or escape.include?(m)
      end
    end

    @stack = []
    @out = ''
  end

  def method_missing(o, *a, &b)
    o = o.to_s

    if b
      @stack << @out
      @out = ''
      @instance_eval.call(&b)
      s = @out
      @out = @stack.pop
      a.unshift(s)
    end

    if o[0,1] == '_'
      m = o[1..-1].to_sym
      @out << @module.send(m, *a, &b).to_s
    else
      o = o.chomp('!') if o[-1,1] == '!'
      @out << @module.send(@constructor, o, *a).to_s
    end
  end

  def to_s() @out end
  def to_str() @out end

  # Could improve.

  def inspect
    r = super
    i = r.index(',')
    return r[0...i] + ">"
  end

end


#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#

# TODO

require 'facets/more/xmlhelper.rb'

      build = BuildingBlocks.new(XMLHelper, :element)

      doc = build.html do

          head do
            title "Test"
          end

          body do
            i "Hello"
            br
            _text "Test"
            _text "Hey"
            _p "Output, Baby!"
          end

      end

puts doc
p build
puts build


      r = "<html><head><title>Test</title></head><body><p>Hello</p>Test</body></html>"
      #assert_equal( r, doc )
      #assert_equal( r, x.to_s )
    #end



#=begin



#=end

