################################################################################
# myagent.rb is self-modifying code. It is a framework for creating new agents.
#
# To run:
#
# C:\trane\myagent>ruby myagent.rb
# > hello
# Default response.
#
# > add pattern /^hello/, hello
#
#
# > hello
# Default response from hello
#
# > >   def hello(input=nil, pattern=nil)
# > >     if @count == nil
# > >       @count = 1; return 'Hello. I am ready for my first lesson.'
# > >     end
# > >     return 'Hello again.'
# > >   end # method_def
# 
#
# > hello
# Hello. I am ready for my first lesson.
#
# > hi is a synonym for hello
# OK, (?-mix:^hi) has been added, and will now call hello.
#
# > hi
# Hello again.
#
# >
################################################################################

require 'yaml'

class MyAgent 

   def initialize
     @filename = File.expand_path(File.dirname(__FILE__)) + "/patterns.yaml"
     unless load_message = load_synonyms
       @patterns = [ [/^(.*?) is a synonym for (.*)/, :add_synonym], 
                     [/^save syn/, :save_synonyms],
                     [/^load syn/, :load_synonyms], 
                     [/^add pattern (.*), (.*)/, :add_pattern], 
                     [/^>(.*)/, :defining_method] 
                   ]
       save_synonyms # creates patterns.yaml on first use.
     else
       puts load_message 
     end
     @method_def = ''
   end

   def def_method method_def
     file = IO.read(__FILE__)
     return "Doesn't have 'end # class'" unless file =~ /(.*)end # class(.*)if \$0 == __FILE__(.*)/m
     grp1 = $1.to_s
     grp2 = $2.to_s
     grp3 = $3.to_s

     new_filename = get_new_filename
     File.open(new_filename, "w+") { |f|
        f.puts grp1 + method_def + "\n\nend # class\n\nif $0 == __FILE__" + grp3
     }
     @patterns.push( [@new_regexp, @new_symbol] )
     save_synonyms
     MyAgent.class_eval(method_def) # not working when client code below is left uncommented out.
   end

   # If this file has a '.' + a number before the '.rb' extension 
   # (i.e. foo.0.rb), increment the number (i.e. return foo.1.rb).
   # Otherwise return the name of this file unchanged.
   def get_new_filename
      old_filename = __FILE__
      if old_filename =~ /(.*)\.(\d+)\.rb$/
        incr = $2.to_i + 1
        return new_filename = $1.to_s + "." + incr.to_s + ".rb"
      end
      return old_filename  # Dangerous!
   end

   # Match input against each regexp pattern in @patterns; 
   # if there's a match, call the method associated with the pattern.
   def getResponse input
     r = "Default response."
     @patterns.each { |pattern|
       if input =~ pattern[0]
          return self.send(pattern[1], pattern[0], input) rescue return r
       end
     }
     return r
   end

   def load_synonyms(pattern=nil, input=nil)
     if File.exist?(@filename)
        @patterns = YAML.load_file(@filename)
        return "I have loaded #{@filename}."
     end 
     return false
   end

   def save_synonyms(pattern=nil, input=nil)
      r = "Eep! error saving synonyms! "
      begin
        File.open(@filename, 'w') do |out|
          YAML.dump(@patterns, out)
        end
        return "OK I have saved the synonyms to disk."
      rescue => error
        return r << error
      end
   end

   def add_synonym(pattern, input)
     return "Problem matching pattern." unless input =~ pattern
     grp1 = %r{^#{$1}}
     grp2 = %r{^#{$2}}
     method =''
     @patterns.each { |p| 
       if p[0] == grp2
          method = p[1]
          break
       end
     }
     return unless method.to_s != nil
     @patterns.push( [grp1, method] )
     save_synonyms
     return "OK, #{grp1} has been added, and will now call #{method}." 
   end

   def add_pattern(pattern=nil, input=nil)
     if input =~ pattern 
       @new_regexp = $1.to_s
       @new_symbol = $2.to_s
       @new_regexp.gsub!(/\//, '')
       @new_regexp = %r{#{@new_regexp}}
       method_def = "   def #{@new_symbol}(pattern=nil, input=nil)\n     return 'Default response from " + @new_symbol + "'\n   end # method_def"
       return def_method(method_def)
     else
       return "#{input} doesn't match #{pattern}."
     end
   end

   def defining_method(pattern=nil, input=nil)
     if input =~ pattern
       @method_def << "\n " << $1.to_s
       if @method_def =~ /end # method_def/
         return def_method(@method_def)
       end
       return ''
     else
       return "#{input} doesn't match #{pattern}."
     end
   end

   def method_missing input, *rest
     return getResponse(input.to_s) 
   end

end # class

if $0 == __FILE__
  bot = MyAgent.new
  print "\n> "; $stdout.flush
  while (line = gets) !~ /^:q/ and line !~ /^$/
    print bot.send(line.to_s).to_s + "\n\n"
    print "> "; $stdout.flush
  end
end # of file