# This AI related code was written by Gregory Brown
# In order to understand it, I had to write comments. I hoped that Ruby is more similar to Python than it actually is :(
# Note that I used Python syntax highlighter, so colors may not be exactly those that you are used to in Ruby
 
class Conversation

  def initialize(person)
    @person = person  #  so far so good. learned what @ is for
    @response_id = 0  #  initial response will be from id 0 pattern match
  end 

  attr_reader :response_id  #  defines getter method

  def say(msg)
    print "#{@person}: " 
    @response_id = Response[@response_id].respond_to(msg)  #  when this is called, responses are already defined; each new response updates current id
  end

  class Response  #  subclass

    def self.responses
       @responses ||= {}  #  is this 'OR' assignment?
    end

    def self.[](id)  #  self.[]?
      responses[id]  #  what happens here?
    end 

    def initialize(id)
      @id = id
      self.class.responses[id] = self  #  creates instances of subclasses for each id ?
                #  clears arrays for each new instance:
      @matchers = []  #  word patterns to recognize and to respond to ...->
      @messages = []  #  ...-> with these massages
    end               

    attr_reader :id,:matchers   #  defines getter method

    def when(pattern,id)
      @matchers << [pattern,id]  #  add pattern to matchers array
    end     

    def inherits(arr)
      arr.each do |id|
        @matchers += Response[id].matchers  #  add inherited patterns to matchers array
      end
    end

    def might_say(msg,weight=1)
      weight.times do
        @messages << msg  #  add msg to messages array
      end
    end       

    def talk
      puts @messages.sort_by { rand }.pop  #  say random message
    end

    def respond_to(msg)
      @matchers.each do |pattern,id|  #  for each matchers if similar to msg, talk and return id.
        if msg =~ pattern
          Response[id].talk  #  response(id) is given, not the one where pattern was matched!
          return id  #  update response_id in say function
        end
      end
      return @id  #  otherwise, no pattern match, and remain silent
    end

  end     

end           

def response(id)
  r = Conversation::Response[id] || Conversation::Response.new(id)  #  if doesn't exist, define new response
  yield(r)
end


#  Example use

c = Conversation.new("Ralph") 

#  define few possible responses for different pattern matches

response(0) { |r|
r.might_say "Hello"  #  these 2 might_say's are never really considered! responses always come from 1, 3, or 4
r.might_say "Hi"
r.when /Hello/i, 1  #  if has case insensitive 'hello' in the sentence, update id to 1
}


response(1) { |r|
r.might_say "Hello"
r.might_say "Hi"
r.might_say "Howdy"
r.when /Hello/i, 4  #  if has case insensitive 'hello' in the sentence, update id to 4
r.when /./, 3  #  anything else, asign id 3
}


response(3) { |r|
r.might_say "Finally, something interesting"
r.inherits [1]
#  or:
#  r.might_say "Hello"
#  r.might_say "Hi"
#  r.might_say "Howdy"
#  r.when /Hello/i, 4
#  r.when /./, 3
}


response(4) { |r|
r.might_say "You already said that, didn't you?"
r.inherits [1]
}

#  finally, execution: tracking backwards all the steps, from say -> respond_to -> talk

loop do
print "Greg: "
r = gets  #  user input
c.say(r)  #  find response with pattern match and current id (0 at begining)
end