CoralMUD-0.15/
CoralMUD-0.15/core/
CoralMUD-0.15/data/
CoralMUD-0.15/data/areas/
CoralMUD-0.15/data/help/
CoralMUD-0.15/data/players/
CoralMUD-0.15/lib/automap/
CoralMUD-0.15/lib/items/
# filters for transforming an argument to something else.
class Filter
  include Singleton

  def filt_to_itype str
    found = query_parse(str, $item_types, false, {:name=>:to_s, :id=>:__id__})
    if found.empty?
      return nil
    end

    return found.collect{|t| item_attribute(t) }
  end

  def filt_to_area str, namespace=nil
    found = Tag.find_any_obj str
    if !found
      return nil
    end
    return found[0]
  end

  def filt_to_int str, namespace=nil
    return Integer(str) rescue nil
  end

  def filt_to_tag str, namespace=nil
    s = str.derive_tag.revert_tag # hack to make sure we don't allow name spaces.  It's not preserved over the conversion.
    return [s, namespace]
  end

  def filt_to_flag str, key
    s = str.multi_args # into each word

    return nil if s[0] == nil || key.include?(s[0].downcase.to_sym) == false
    [s[0].downcase.to_sym, s[1]] # return a valid flag and an optional value to set it to.
  end

  def filt_to_exit old
    s = old.split
    if s[0] == nil || s[0].downcase.exit_code_to_i == nil
      return nil
    end
    [s[0].downcase.exit_code_to_i, s[1]]
  end

  def filt_none old
    return old
  end

  def filt_to_sect old
    if Sector.lookup(old) # returns nil on a fail
      Sector.lookup(old).symbol
    else
      nil
    end
  end
end

EditorFilters = Filter.instance


### Attached to any class that can be edited automatically with the variable "editor_interface"
class Editor
  attr :lookup
  @@editor_list = {}  ### stored across all editors in existance.  Elegant way to have something to check input against.
  
  def initialize name, p, p_on_exit,  c

    @lookup = p 

    @on_exit = p_on_exit

    @@editor_list[name] = self
    @editor_name = name    ### the editors name, such as redit
    
    @editor_commands = [EditorCommand.new("show", nil, :arg_none, :filt_none, proc{|ed, ch| ch.text_to_player("#{ed.display_values ch.editing}") },nil,nil, self),
                        EditorCommand.new("commands", nil, :arg_none, :filt_none, proc{|ed, ch| ch.text_to_player("#{ed.commands_list_get}" + ENDL)}, nil,nil, self),
                        EditorCommand.new("done", nil, :arg_none, :filt_none, 
                                proc{|ed, ch| ch.text_to_player("Leaving editor."+ENDL);e = ch.editing.shift; @on_exit.call(e) if @on_exit}, nil,nil, self)]
  end

  def add_command command_arg
    @editor_commands << command_arg
  end

  def self.list
    @@editor_list
  end

  # return a string to display the current values in the object being edited.
  def display_values objs
    obj = objs[0]
    a = @editor_commands.collect do |c| 
      s = nil
      if c.function != nil
        s = "#R[#{mxptag('send "&text;" prompt')}#W#{c.name}#{mxptag('/send')}#R]:" + ' '*(20 - c.name.length - 3)

        if c.pword_hidden
          s << " #W*****"
        else
          case c.type
            when :flags then s << "#W#{obj.instance_variable_get("@#{c.name}").display_flags(c.key, c.name, 20)}"
            when :namespace then s << "#W#{obj.namespace}"
            else s << " #W#{obj.instance_variable_get("@#{c.name}")}"
          end
        end
      end
      s
    end
    z = @editor_commands.select {|com| com.proc_display != nil}.collect { |c| c.call_display nil, obj }

    if !z.empty?
      a << "#R=========================================================================="
      a += z
    end

    str = '#R__________________________________________________________________________' + ENDL
    if obj.is_a?(String)
      str<<("#R_%s_" % "#{@editor_name}: #{obj[0..30]}[...]").ljust(74, '_') + ENDL
    else
      str<<("#R_%s_" % "#{@editor_name}: #{obj}".strip_mxp!.upcase).ljust(74, '_') + ENDL
    end

    a.compact!
    str += a.join(ENDL) + ENDL
    str +="#R==========================================================================" + ENDL
    str +="#R[#{mxp('send')}#Wdone#{mxp('/send')}#R]#n" + ENDL
  end

  def commands_list_get
    @editor_commands.collect { |c|  c.name }
  end

  ### selects all commands that match arg even partially. 
  def find_command arg
    @editor_commands.select {|c| c.name==arg} 
  end
end

# a single command in the editor_commands tables per Editor created.
class EditorCommand 
  attr :name, :function, :arg_type, :proc_fun, :proc_display, :key, :type, :pword_hidden
  def initialize name, function, arg_type, filter, p, d,key, editor, hidden = false, type=:set, pword_hidden=false
    @key = key # for passing optional filter arguments.  For example, the correct table for the :filter_flag filter.
    @name = name
    @function = function
    @arg_type = arg_type
    @filter = filter
    @hidden = hidden
    @proc_fun = p
    @proc_display = d
    @editor = editor
    @type = type
    @pword_hidden = pword_hidden
  end

  def to_str
    "EditorCommand: #{name}"
  end

  # use this commands overall filter to return the correct type.
  def filter arg, player=nil
    if @type == :vtag
      return EditorFilters.send(@filter, arg, nil)
    end

    if @key != nil
      EditorFilters.send(@filter, arg, @key)
    else
      EditorFilters.send(@filter, arg)
    end
  end

  def call_display ch, obj
    if @proc_display
      @proc_display.call obj
    end
  end

  def call_fun ch, obj, arg
    if @proc_fun != nil
      @proc_fun.call @editor, ch, obj, arg
    else
      ch.text_to_player "Value set." + ENDL
      obj.send(@function, arg)
    end
  end
end

### filters
class String
  ### take a string and return a filtered string. Should really take no modifications.
  def str_to_str
    self
  end
end

$editor_lookup_procs = {}  # map of editor to procs for looking up values.
                           # this is initialized when you construct the editor.

$loadable_classes = {}
#meta programming to define our editor functions and accessors.
class Class  
  # defines that this class can be created with the create command.
  def define_creatable
    log :debug, "Creatable class: #{self.to_s.downcase}=>#{self}"
    $editable_classes[self.to_s.downcase] = self # all classes that may be edited. 
  end

  # defines that this class can be loaded with the load command.
  def define_loadable
    log :debug, "Loadable class: #{self.to_s.downcase}=>#{self}"
    $loadable_classes[self.to_s.downcase] = self
  end

  def define_editor sym=:none, h={}
    p = h[:lookup]
    p_on_exit = h[:on_exit]
    class_variable_set("@@class_editor", Editor.new(sym.to_s, p, p_on_exit, self))
    define_method("class_editor") do
      self.class.class_variable_get(:@@class_editor)
    end
    if p
      ### adds the editor name to the class.   For example "redit"
      $editor_lookup_procs[sym] = p
      ### add command to global command table for wizards.
#      $tabWizCmd << Command.new(sym.to_s,  [[:arg_none], [:arg_int!]])

#      Player.send(:define_method, "cmd_#{sym}") do |tab_entry, arg|
#        begin
#          found = $editor_lookup_procs[sym].call(arg, self) # call the proc defined to lookup values.  
#        rescue Exception=>e
#          log_exception e
#          text_to_player "Failed on lookup."
#        end
#        return if found == nil

#        a = instance_variable_get(:@editing) || []

#        a << found
               
#        instance_variable_set(:@editing,a) 
#        text_to_player "Success!! You're currently editing #{@editing[0]}." + ENDL
#        execute_command("show")
#      end
    end
  end

  def define_editor_field(*accessors)
    accessors.each do |each_hash|
      a_symbol = each_hash[:name] # The name of the variable
      arg_type = each_hash[:arg_type] || :arg_str  # Type of argument that is expected to be supplied.
      filter = each_hash[:filter] # filter to use
      p = each_hash[:proc_fun] # if this isn't nil we should use it instead of defining a method.
      d = each_hash[:display] 
      key = each_hash[:filter_key]
      type = each_hash[:type] || :set
      hidden_value = each_hash[:hidden] || false      

      temp = class_variable_get(:@@class_editor);           
      e = temp
      if p
        temp.add_command EditorCommand.new(a_symbol, nil, arg_type, filter, p, d, key, e, false, type, hidden_value)
      else
        temp.add_command EditorCommand.new(a_symbol, "edit_#{a_symbol}".to_sym, arg_type, filter, p, d, key, e, false, type, hidden_value)

        case type
        when :vtag
          define_method("edit_#{a_symbol}".to_sym) do |val|
            #we expect val to be a tag. I.e. a single word
            # possibly:  a.simple.tag 
            if val[1] == nil
              send(:assign_tag, val[0], instance_variable_get('@namespace'))  # object must mix-in virtual tags
            else
              send(:assign_tag, val[0], val[1])
            end
            true
          end
        when :flags
          define_method("edit_#{a_symbol}".to_sym) do |val|
            # we expect val to be an array with a flag and an optional value.
            # If the value is nil we toggle the flag.  If the value is not nil we set the flag to that value.
            temp = instance_variable_get("@#{a_symbol}")
            

            if val[1] == nil
              if temp == nil
                temp = {}
                temp.toggle(val[0])
                instance_variable_set("@#{a_symbol}", temp)
              else
                temp.toggle val[0]
              end
            else
              temp[val[0]] = val[1]
              true
            end   
            # true returned if flag is set.  false if flag is off. 
          end
        when :namespace
          define_method("edit_#{a_symbol}".to_sym) do |val|
            # We can assume the data is already correct coming in.
            reassociate_tag(val) # 
            true
          end
        when :set
          # room.edit_name "Name of a Room"
          define_method("edit_#{a_symbol}".to_sym) do |val|
            # We can assume the data is already correct coming in.
            instance_variable_set("@#{a_symbol}", val)  
            true
          end
        end

      end
    end
  end
end