# = dev-utils/debug/log.rb
#
# DevUtils::Debug.{debug,trace,logger=,logfile=} are implemented here.  Do not load this file
# directly.
#

require 'logger'
require 'extensions/binding'
require 'extensions/object'

module DevUtils::Debug

    # The target of logging messages from _debug_ and _trace_.
  DEBUGLOG = Logger.new(File.new('debug.log', 'w'))
  DEBUGLOG.datetime_format = " \010"
  DEBUGLOG.progname = "\010\010\010"

    #
    # Write _message_ to the debugging log.
    #
    # The <em>debugging log</em> is a zero-conf logfile.  Here is an example usage:
    #
    #   $ cat example.rb
    #
    #   require 'dev-utils/debug'
    #
    #   debug "Setting variables x and y"
    #   x = 5; y = 17
    #   trace 'x + y'
    #   puts "Finished"
    #
    #   $ ruby example.rb
    #   Finished
    #
    #   $ cat debug.log
    #   D, [#244] DEBUG : Setting variables x and y
    #   D, [#244] DEBUG : x + y = 22
    #   
    # Simply with <tt>require 'dev-utils/debug'</tt>, you have availed yourself of a handy
    # debugging log file which you don't have to create.
    #
    # See also the _trace_ method.
    #
  def debug(message)
    DEBUGLOG.debug message
  end

    #
    # Prints a trace message to DEBUGLOG (at debug level).  Useful for emitting
    # the value of variables, etc.  Use like this:
    #
    #   x = y = 5
    #   trace 'x'        # -> 'x = 5'
    #   trace 'x ** y'   # -> 'x ** y = 3125'
    #
    # If you have a more complicated value, like an array of hashes, then you'll probably want
    # to use an alternative output format.  For instance:
    #
    #   trace 'value', :yaml
    #
    # Valid output format values (the _style_ parameter) are:
    #
    #   :p :inspect
    #   :pp                     (pretty-print, using 'pp' library)
    #   :s :to_s
    #   :y :yaml :to_yaml       (using the 'yaml' library')
    #
    # The default is <tt>:p</tt>.
    #
  def trace(expr, style=:p)
    unless expr.respond_to? :to_str
      message = "trace: Can't evaluate the given value: #{caller.first}"
      DEBUGLOG.warn message
    else
      Binding.of_caller do |b|
        value = b.eval(expr.to_str)
        formatter = TRACE_STYLES[style] || :inspect
        case formatter
        when :pp then require 'pp'
        when :y, :yaml, :to_yaml then require 'yaml'
        end
        value_s = value.send(formatter)
        message = "#{expr} = #{value_s}"
        lines = message.split(/\n/)
        indent = "   "
        DEBUGLOG.debug lines.shift            # First line unindented.
        lines.each do |line|
          DEBUGLOG.debug(indent + line)       # Other lines indented.
        end
      end
    end
  end
  TRACE_STYLES = {}  # :nodoc:
  TRACE_STYLES.update( :pp => :pp_s, :s => :to_s, :p => :inspect, :y => :to_yaml,
                       :yaml => :to_yaml, :inspect => :inspect, :to_yaml => :to_yaml )

    # Planned feature; not yet implemented.
  def logger=(x)
    warn 'logger= is not implemented; ignoring'
  end

    # Planned feature; not yet implemented.
  def logfile=(x)
    warn 'logfile= is not implemented; ignoring'
  end

end  # module DevUtils::Debug