#==============================================================================|
#  ** Script Info                                                              |
#------------------------------------------------------------------------------|
#  * Script Name                                                               |
#    DoubleX RMVXA Object Trace                                                |
#------------------------------------------------------------------------------|
#  * Functions                                                                 |
#    Traces all objects meeting some conditions linked to the queried object   |
#    Designed as a bug diagnosis tool used by scripters with debug experience  |
#------------------------------------------------------------------------------|
#  * Example                                                                   |
#    obj.inspect                                                               |
#    - [url]http://pastebin.com/kb1q1Dru[/url]                                            |
#    obj.trace_obj(Proc); obj.obj_trace[Proc].inspect                          |
#    - [url]http://pastebin.com/PZ4KNbHv[/url]                                            |
#------------------------------------------------------------------------------|
#  * Terms Of Use                                                              |
#    You shall keep this script's Script Info part's contents intact           |
#    You shalln't claim that this script is written by anyone other than       |
#    DoubleX or his aliases                                                    |
#    None of the above applies to DoubleX or his aliases                       |
#------------------------------------------------------------------------------|
#  * Prerequisites                                                             |
#    Abilities:                                                                |
#    1. Basic knowledge of inspecting objects in Ruby                          |
#    2. Some RGSS3 scripting proficiency to fully utilize this script          |
#------------------------------------------------------------------------------|
#  * Instructions                                                              |
#    1. Open the script editor and put this script into an open slot between   |
#       Materials and Main, save to take effect.                               |
#------------------------------------------------------------------------------|
#  * Links                                                                     |
#    Script Usage 101:                                                         |
#    1. forums.rpgmakerweb.com/index.php?/topic/32752-rmvxa-script-usage-101/  |
#    2. rpgmakervxace.net/topic/27475-rmvxa-script-usage-101/                  |
#    This script:                                                              |
#    1. [url]http://pastebin.com/pySjrKvh[/url]                                           |
#    Mentioned Patreon Supporters:                                             |
#    [url]https://www.patreon.com/posts/71738797[/url]                                    |
#------------------------------------------------------------------------------|
#  * Author                                                                    |
#    DoubleX                                                                   |
#------------------------------------------------------------------------------|
#  * Changelog                                                                 |
#    v1.02a(GMT 0100 27-10-2015):                                              |
#    1. Lets uers set the conditions and labels for tracing objects            |
#    v1.01b(GMT 1300 13-7-2015):                                               |
#    1. Fixed not tracing klass linked to klass linked to queried object bug   |
#    2. Fixed some Script Call Info and Implementation documentation typos     |
#    3. Added more info in Prerequisites and Script Call Info                  |
#    4. Increased this script's compactness                                    |
#    v1.01a(GMT 1300 6-7-2015):                                                |
#    1. Instance methods can be traced as well                                 |
#    v1.00b(GMT 0800 1-7-2015):                                                |
#    1. Fixed object having Range and/or Structs not tracing klass properly bug|
#    v1.00a(GMT 1200 27-6-2015):                                               |
#    1. 1st version of this script finished                                    |
#==============================================================================|

#==============================================================================|
#  ** Script Call Info                                                         |
#     A path in the object trace will stop if it'd be cyclic                   |
#------------------------------------------------------------------------------|
#  * Object manipulations                                                      |
#    1. trace_obj(cond, label)                                                 |
#       - Traces all objects meeting cond method linked to this object         |
#       - Labels all traced objects using label method                         |
#       - cond and label are method symbols in Object Trace Condition Method   |
#         and Object Trace Label Method respectively                           |
#    2. obj_trace[cond]                                                        |
#       - Returns all traced objects meeting cond method linked to this object |
#       - cond is a method symbol in Object Trace Condition Method             |
#    3. (v1.01a+)trace_idef                                                    |
#       - Traces all instance methods linked to this object                    |
#    4. (v1.01a+)idef_trace                                                    |
#       - Returns the trace of all instance methods linked to this object      |
#==============================================================================|

($doublex_rmvxa ||= {})[:Obj_Trace] = "v1.02a"

module DoubleX_RMVXA # v1.02a+

  module Obj_Trace

    #--------------------------------------------------------------------------|
    #  Object Trace Condition Method                                           |
    #  - Setups cond used by trace_obj(cond, label)                            |
    #--------------------------------------------------------------------------|
    # cond must be the symbol of a method taking the currently traced object as
    # the only arguement
    # The below examples are added to help you setup your own cond methods

    # Checks if the currently traced object belongs to klass
    def self.cond_klass(obj)
        obj.is_a?(klass)
    end # cond_klass

    # Add your own cond methods here
    

    #--------------------------------------------------------------------------|
    #  Object Trace Label Method                                               |
    #  - Setups label used by trace_obj(cond, label)                           |
    #--------------------------------------------------------------------------|
    # label must be the symbol of a method taking the currently traced object as
    # the only arguement
    # The below examples are added to help you setup your own label methods

    # Labels all traced objects using their class symbol
    def self.label_klass(obj)
        :"#{obj.class}"
    end # label_klass

    # Add your own label methods here
    

  end # Obj_Trace

end # DoubleX_RMVXA

#==============================================================================|
#  ** Script Implementations                                                   |
#     You need not edit this part as it's about how this script works          |
#------------------------------------------------------------------------------|
#  * Script Support Info:                                                      |
#    1. Prerequisites                                                          |
#       - Solid understanding of inspecting objects in Ruby                    |
#       - Decent RGSS3 scripting proficiency to fully comprehend this script   |
#    2. Method documentation                                                   |
#       - The 1st part describes why this method's rewritten/aliased for       |
#         rewritten/aliased methods or what the method does for new methods    |
#       - The 2nd part describes what the arguments of the method are          |
#       - The 3rd part informs which version rewritten, aliased or created this|
#         method                                                               |
#       - The 4th part informs whether the method's rewritten or new           |
#       - The 5th part informs whether the method's a real or potential hotspot|
#       - The 6th part describes how this method works for new methods only,   |
#         and describes the parts added, removed or rewritten for rewritten or |
#         aliased methods only                                                 |
#       Example:                                                               |
# #--------------------------------------------------------------------------| |
# #  Why rewrite/alias/What this method does                                 | |
# #--------------------------------------------------------------------------| |
# # *argv: What these variables are                                            |
# # &argb: What this block is                                                  |
# def def_name(*argv, &argb) # Version X+; Rewrite/New; Hotspot                |
#   # Added/Removed/Rewritten to do something/How this method works            |
#   def_name_code                                                              |
#   #                                                                          |
# end # def_name                                                               |
#------------------------------------------------------------------------------|

class Object # Edit

  #----------------------------------------------------------------------------|
  #  New public instance variables                                             |
  #----------------------------------------------------------------------------|
  attr_reader :idef_trace # (v1.01a+)The trace of all linked instance methods
  attr_reader :obj_trace # The traces of all objects linked to this object

  # (v1.01a+)The list of symbols of all instance variables added by this script
  OBJ_TRACE_IVAR = [:"@idef_trace", :"@obj_trace"]

  def trace_idef # v1.01a+; New
    # Stop tracing the object if the object trace path would be cyclic
    @idef_trace ? return : @idef_trace = {}
    #
    trace_instance_idef
    return trace_array_idef if is_a?(Array)
    return trace_hash_idef if is_a?(Hash)
    return trace_range_idef if is_a?(Range)
    trace_struct_idef if is_a?(Struct)
  end # trace_idef

  def trace_instance_idef # v1.01a+; New
    (instance_variables - OBJ_TRACE_IVAR).each { |ivar|
      traverse_idef_tree(ivar, instance_variable_get(ivar))
    }
  end # trace_instance_idef

  def trace_array_idef # v1.01a+; New
    each_with_index { |val, index| traverse_idef_tree(index, val) }
  end # trace_array_idef

  def trace_hash_idef # v1.01a+; New
    each { |key, val| traverse_idef_tree(key, val) }
  end # trace_hash_idef

  def trace_range_idef # v1.01a+; New
    index = -1
    each { |val| traverse_idef_tree(index += 1, val) }
  end # trace_range_idef

  def trace_struct_idef # v1.01a+; New
    each_pair { |key, val| traverse_idef_tree(key, val) }
  end # trace_struct_idef

  #----------------------------------------------------------------------------|
  #  Label and use all nonempty subtrees to form the original object trace tree|
  #----------------------------------------------------------------------------|
  # iks: The index/key/symbol of the object trace
  # val: The object to be traced
  def traverse_idef_tree(iks, val) # v1.01a+; New
    # Recursively traverse the object trace tree using Depth First Search
    unless (idefs = val.instance_methods).empty?
      @idef_trace[iks] = [idefs]
    end
    val.trace_idef
    return if (trace = val.idef_trace).empty?
    (@obj_trace[iks] ||= []) << trace
    #
  end # traverse_idef_tree

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_obj(cond, label) # New
    # Stop tracing the object if the object trace path would be cyclic
    (@obj_trace ||= {})[cond] ? return : @obj_trace[cond] = {}
    #
    trace_instance_obj(cond, label)
    return trace_array_obj(cond, label) if is_a?(Array)
    return trace_hash_obj(cond, label) if is_a?(Hash)
    return trace_range_obj(cond, label) if is_a?(Range)
    trace_struct_obj(cond, label) if is_a?(Struct)
  end # trace_obj

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_instance_obj(cond, label) # New
    (instance_variables - OBJ_TRACE_IVAR).each { |ivar|
      trace_all_obj(cond, label, ivar, instance_variable_get(ivar))
    }
  end # trace_instance_obj

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_array_obj(cond, label) # New
    each_with_index { |val, index| trace_all_obj(cond, label, index, val) }
  end # trace_array_obj

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_hash_obj(cond, label) # New
    each { |key, val| trace_all_obj(cond, label, key, val) }
  end # trace_hash_obj

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_range_obj(cond, label) # v1.00b+; New
    # Embeds the klass traces of all ranges linking to this object
    index = -1
    each { |val| trace_all_obj(cond, label, index += 1, val) }
    #
  end # trace_range_obj

  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  def trace_struct_obj(cond, label) # v1.00b+; New
    each_pair { |key, val| trace_all_obj(cond, label, key, val) }
  end # trace_struct_obj

  #----------------------------------------------------------------------------|
  #  Label and use all nonempty subtrees to form the original object trace tree|
  #----------------------------------------------------------------------------|
  # cond: The object trace condition method symbol taking the object as argument
  # label: The object trace label method symbol taking the object as argument
  # iks: The index/key/symbol of the object trace
  # val: The object to be traced
  def trace_all_obj(cond, label, iks, val) # v1.01a+; New
    # Recursively traverse the object trace tree using Depth First Search
    ot = DoubleX_RMVXA::Obj_Trace
    @obj_trace[cond][iks] = [ot.send(label, val)] if ot.send(cond, val)
    val.trace_obj(cond, label)
    return if (trace = val.obj_trace[cond]).empty?
    (@obj_trace[cond][iks] ||= []) << trace
    #
  end # trace_all_obj

end # Object

#------------------------------------------------------------------------------|

#==============================================================================|