class RemindPlugin < Plugin
  # read a time in string format, turn it into "seconds from now".
  # example formats handled are "5 minutes", "2 days", "five hours",
  # "11:30", "15:45:11", "one day", etc.
  #
  # Throws:: RunTimeError "invalid time string" on parse failure
  def timestr_offset(timestr)
    case timestr
      when (/^(\S+)\s+(\S+)$/)
        mult = $1
        unit = $2
        if(mult =~ /^([\d.]+)$/)
          num = $1.to_f
          raise "invalid time string" unless num
        else
          case mult
            when(/^(one|an|a)$/)
              num = 1
            when(/^two$/)
              num = 2
            when(/^three$/)
              num = 3
            when(/^four$/)
              num = 4
            when(/^five$/)
              num = 5
            when(/^six$/)
              num = 6
            when(/^seven$/)
              num = 7
            when(/^eight$/)
              num = 8
            when(/^nine$/)
              num = 9
            when(/^ten$/)
              num = 10
            when(/^fifteen$/)
              num = 15
            when(/^twenty$/)
              num = 20
            when(/^thirty$/)
              num = 30
            when(/^sixty$/)
              num = 60
            else
              raise "invalid time string"
          end
        end
        case unit
          when (/^(s|sec(ond)?s?)$/)
            return num
          when (/^(m|min(ute)?s?)$/)
            return num * 60
          when (/^(h|h(ou)?rs?)$/)
            return num * 60 * 60
          when (/^(d|days?)$/)
            return num * 60 * 60 * 24
          else
            raise "invalid time string"
        end
      when (/^(\d+):(\d+):(\d+)$/)
        hour = $1.to_i
        min = $2.to_i
        sec = $3.to_i
        now = Time.now
        later = Time.mktime(now.year, now.month, now.day, hour, min, sec)
        return later - now
      when (/^(\d+):(\d+)$/)
        hour = $1.to_i
        min = $2.to_i
        now = Time.now
        later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec)
        return later - now
      when (/^(\d+):(\d+)(am|pm)$/)
        hour = $1.to_i
        min = $2.to_i
        ampm = $3
        if ampm == "pm"
          hour += 12
        end
        now = Time.now
        later = Time.mktime(now.year, now.month, now.day, hour, min, now.sec)
        return later - now
      when (/^(\S+)$/)
        num = 1
        unit = $1
        case unit
          when (/^(s|sec(ond)?s?)$/)
            return num
          when (/^(m|min(ute)?s?)$/)
            return num * 60
          when (/^(h|h(ou)?rs?)$/)
            return num * 60 * 60
          when (/^(d|days?)$/)
            return num * 60 * 60 * 24
          else
            raise "invalid time string"
        end
      else
        raise "invalid time string"
    end
  end

  def initialize
    super
    @reminders = Hash.new
  end
  def cleanup
    @reminders.each_value {|v|
      v.each_value {|vv|
        @bot.timer.remove(vv)
      }
    }
    @reminders.clear
  end
  def help(plugin, topic="")
    "reminder plugin: remind <who> [about] <message> in <time>, remind <who> [about] <message> every <time>, remind <who> [about] <message> at <time>, remind <who> no more [about] <message>, remind <who> no more. Generally <who> should be 'me', but you can remind others (nick or channel) if you have remind_others auth"
  end
  def add_reminder(who, subject, timestr, repeat=false)
    begin
      period = timestr_offset(timestr)
    rescue RuntimeError
      return "couldn't parse that time string (#{timestr}) :("
    end
    if(period <= 0)
      return "that time is in the past! (#{timestr})"
    end
    if(period < 30 && repeat)
      return "repeats of less than 30 seconds are forbidden"
    end
    if(!@reminders.has_key?(who))
      @reminders[who] = Hash.new
    elsif(@reminders[who].has_key?(subject))
      del_reminder(who, subject)
    end

    if(repeat)
      @reminders[who][subject] = @bot.timer.add(period) {
        tstr = (Time.now + period).strftime("%H:%M:%S")
        @bot.say who, "repeat reminder (next at #{tstr}): #{subject}"
      }
    else
      @reminders[who][subject] = @bot.timer.add_once(period) {
        tstr = Time.now.strftime("%H:%M:%S")
        @bot.say who, "reminder (#{tstr}): #{subject}"
      }
    end
    return false
  end
  def del_reminder(who, subject=nil)
    if(subject)
      if(@reminders.has_key?(who) && @reminders[who].has_key?(subject))
        @bot.timer.remove(@reminders[who][subject])
        @reminders[who].delete(subject)
        return true
      else
        return false
      end
    else
      if(@reminders.has_key?(who))
        @reminders[who].each_value {|v|
          @bot.timer.remove(v)
        }
        @reminders.delete(who)
        return true
      else
        return false
      end
    end
  end
  def remind(m, params)
    who = params.has_key?(:who) ? params[:who] : m.sourcenick
    string = params[:string].to_s
    debug "in remind, string is: #{string}"
    if(string =~ /^(.*)\s+in\s+(.*)$/)
      subject = $1
      period = $2
      if(err = add_reminder(who, subject, period))
        m.reply "incorrect usage: " + err
        return
      end
    elsif(string =~ /^(.*)\s+every\s+(.*)$/)
      subject = $1
      period = $2
      if(err = add_reminder(who, subject, period, true))
        m.reply "incorrect usage: " + err
        return
      end
    elsif(string =~ /^(.*)\s+at\s+(.*)$/)
      subject = $1
      time = $2
      if(err = add_reminder(who, subject, time))
        m.reply "incorrect usage: " + err
        return
      end
    else
      usage(m)
      return
    end
    m.okay
  end
  def no_more(m, params)
    who = params.has_key?(:who) ? params[:who] : m.sourcenick
    deleted = params.has_key?(:string) ? 
              del_reminder(who, params[:string].to_s) : del_reminder(who)
    if deleted
      m.okay
    else 
      m.reply "but I wasn't going to :/"
    end
  end
end
plugin = RemindPlugin.new

plugin.default_auth('other', false)

plugin.map 'remind me no more', :action => 'no_more'
plugin.map 'remind me no more [about] *string', :action => 'no_more'
plugin.map 'remind me [about] *string'
plugin.map 'remind :who no more', :auth_path => 'other', :action => 'no_more'
plugin.map 'remind :who no more [about] *string', :auth_path => 'other', :action => 'no_more'
plugin.map 'remind :who [about] *string', :auth_path => 'other'