# Wrapper for K5agent, adding user commands to modify the score, etc. 

require './K5agent'

class MyBot 

   def initialize
      print "Hello\n"
      @agent = MyAgent.new
      @THRESHHOLD = 1; if $0 == __FILE__ then @THRESHHOLD = 0 end
      @SCORE_MOD = 0
      @MAX_RESPONSE = 10000
      @MY_NAME = "(?i)(?:K5agent|K5bot)"

      @DEBUG = 0
      @agent.DEBUG = @DEBUG

      @rules = [] 
      @SAFE_LEVEL = "low"
      @Low_safe_level_words = ["low", "default", "0", "off"]
      @High_safe_level_words = ["high", "on", "1"]
   end

   def getResponse(input)

      input = input.to_s
      #print 'INPUT==' + input + "\n"

      input = apply_preprocessing_if_rules(input)
      puts "input after preprocessing rules: #{input}" unless @DEBUG == 0

      # Set threshhold.
      if input =~ /^what is your name?/i
         name = @MY_NAME.gsub(/\(\?i\)\(\?\:/, '')
         name.gsub!(/\|/, ' or ')
         name.gsub!(/\)/, '')
         return name.to_s
      end

      if input =~ /^#{@MY_NAME}[:,!]* (quiet|be quiet|stfu|shut.*up)/
         @THRESHHOLD = @THRESHHOLD + 1
         @SCORE_MOD -= 1
         return "My threshhold is now " << @THRESHHOLD.to_s << ", and my score_mod is #{@SCORE_MOD}."
      end
      if input =~ /^#{@MY_NAME}[:,!]* (speak.*up|loud|louder|i can't hear you)/
         @THRESHHOLD = @THRESHHOLD - 1
         @SCORE_MOD += 1
         return "My threshhold is now " << @THRESHHOLD.to_s << ", and my score_mod is #{@SCORE_MOD}."
      end
      # Return threshhold
      if input =~ /^#{@MY_NAME}[:,!]* what is your (threshhold|threshold|volume)/ or input =~ /^#{@MY_NAME}[:,!]* (?:threshold|threshhold|volume)/
         return "My threshhold is currently " << @THRESHHOLD.to_s
      end

      # Provide help message.
      if input =~ /^#{@MY_NAME}[:,!]* help/
         return "I am K5Bot."
      end

      # trap restart
      case input
        when /^#{@MY_NAME}[:,!]* restart/, /^restart/
          #return "That command is disabled." # for safety
          exec("ruby #{__FILE__}")
      end

      # add new if-then rules to post-process responses
      case input
        when /^#{@MY_NAME}[:,!]* (if .*? =~ .*? then .*)$/
          return add_if_rule($1)
      end

      # show the rules
      case input
        when /^#{@MY_NAME}[:,!]*.* show.* rules/
          return show_rules[0]
      end

      # delete all rules
      case input
        when /^#{@MY_NAME}[:,!]*.* delete.* rules/
          return delete_rules
      end

      # delete a rule
      case input
        when /^#{@MY_NAME}[:,!]*.* delete.* rule.* (\d+)/
          return delete_rule($1.to_i)
      end

      # set safe level
      case input
        when /^#{@MY_NAME}[:,! ]*(?:set )?.*safe(?:ty)? level (?:to )?(.*)/
          return set_safe_level($1)
      end

      # get safe level
      case input 
        when /^#{@MY_NAME}[:,! ]*what is.*?safe(?:ty)? level/
          return  get_safe_level
      end

      # Set DEBUG level
      case input
        when /^#{@MY_NAME}[:,!]*.*on ?(?:the )?debug(?: mode)?/i,
             /^#{@MY_NAME}[:,!]*.*debug(?: mode)? on/i
          @DEBUG = 1; @agent.DEBUG = 1; return "I have turned on debug output."
        when /^#{@MY_NAME}[:,!]*.*off ?(?:the )?debug(?: mode)?/i,
             /^#{@MY_NAME}[:,!]*.*\bdebug\b.*\boff\b/i
          @DEBUG = 0; @agent.DEBUG = 0; return "I have turned debug output off."
        when /^what is the debug mode/i, 
             /^#{@MY_NAME}[:,!]*.*what is (?:the )?debug(?: mode|setting|level)?/
          setting = "on"
          if @DEBUG == 0 then setting = "off" end
          return "The debug mode is currently #{setting}."
      end

      # Get the agent's response.
      if input =~ /^#{@MY_NAME}[:,! ]*(.*)/ then input = $1.strip end
      puts "input==#{input}" unless @DEBUG == 0
      r, s = @agent.getResponse(input)
      if s == nil then s = 1 end
      r, s = apply_if_rules(r,s,input)
      s += @SCORE_MOD
      if r.to_s.size > @MAX_RESPONSE
         r = r[0..@MAX_RESPONSE]
      end
      puts "\n[K5Bot] score==#{s}; threshhold==#{@THRESHHOLD}" unless @DEBUG == 0
      puts "r == #{r}" unless @DEBUG == 0
      if s > @THRESHHOLD 
        if $0 == __FILE__ then return r end
        puts "[K5Bot] r==#{r}; s==#{s}" unless @DEBUG == 0
        return [r, s]
      else
         #puts 'r==' + r
      end

      return ""
   end

   # Respond to natural language method calls.
   def method_missing input
     return getResponse(input.to_s)
   end

   # add an if-then rule
   def add_if_rule(rule = '')
     if rule == '' then return "No rule found." end
     if @SAFE_LEVEL =~ /#{@Low_safe_level_words.join('|')}$/ 
       @rules.push(rule)
       return "Okay I have added #{rule}."
     end
     return "I can't add any if rules because my safe level is #{@SAFE_LEVEL}."
   end

   def apply_preprocessing_if_rules(input)
     @rules.each { |rule|
       if rule =~ /input.{1,2}sub\!\(/
         if @DEBUG > 0
           puts "[apply_preprocessing_if_rule] rule==#{rule}"
         end
         begin
           eval(rule)
         rescue Exception => e 
           puts "Can't apply rule: #{rule}" unless @DEBUG == 0
           next 
         end
       end
     }
     return input
   end

   # apply if-then rules
   def apply_if_rules(response, score, input)
     @rules.each { |rule|
       if @DEBUG > 0
         puts "rule==#{rule}; response==#{response}; score==#{score}"
       end
       begin
         eval(rule)
       rescue; puts "Can't apply rule: #{rule}" unless @DEBUG == 0; next; end
     }
     #puts "returning: response==#{response}; score==#{score}"
     return [response, score]
   end

   # return the rules
   def show_rules
     return [@rules.join("\n"), 4]
   end

   # delete a rule by its index
   def delete_rule(i)
     if i < 1 or i > @rules.size
       return "The rules go from 1 to #{@rules.size}."
     end
     rule = @rules[i-1]
     @rules.delete_at(i-1)
     return "Okay, \"#{rule}\" deleted."
   end

   # delete all rules
   def delete_rules
     @rules = []
     return "Okay I have deleted all the rules."
   end

   def set_safe_level(level)
     case level 
       when /\b#{@Low_safe_level_words.join('|')}\b/ 
         @SAFE_LEVEL = "off"
         return "Okay I have turned off the safe level."
       when /\b#{@High_safe_level_words.join('|')}\b/
         @SAFE_LEVEL = "on"
         return "Okay I have turned the safe level on."
     end
     return "I don't understand #{level}."
   end

   def get_safe_level
     return "My safe level is set to #{@SAFE_LEVEL}."
   end # method_def

end

if $0 == __FILE__
  b = MyBot.new
  print("\n> "); $stdout.flush
  quitwords = [":q", "quit", "exit", "bye"]
  while (line = gets) !~ /^#{quitwords.join('|')}$/ and line !~ /^$/
    print "#{b.send(line)}\n"
    print("\n> "); $stdout.flush
  end
  b.send("logout")
  print "Bye!"
end
