require 'cinch'
require 'json'
require 'time'
require 'chronic'
require 'open-uri'
require 'nokogiri/class_resolver'
require 'nokogiri'
require 'uri'
require 'net/http'
require 'json'
require 'htmlentities'

load "trivia_master.rb"

def fix_nick(n)
  return n[0] + "\uFEFF" + n[1,n.length]
end

def human_time(time)
  out_str = ""
  
  if time > (60*60*24) then #days
    out_str = (time/(60*60*24)).to_i.to_s + " days"
  elsif time > (60*60) then #hours
    out_str = (time/(60*60)).to_i.to_s + " hours"
  elsif time > (60) then #minutes
    out_str = (time/(60)).to_i.to_s + " minutes"
  else #seconds
    out_str = time.to_s + " seconds"
  end

  return out_str
end

def get_data(f)
  data = JSON.parse(File.read("data.bd"))

  if data[f] == nil then
    return {}
  else
    return data[f]
  end
end

def set_data(f, v)
  data = JSON.parse(File.read("data.bd"))

  data[f] = v

  File.write("data.bd", JSON.generate(data))
end

bot = Cinch::Bot.new do
  pounces = []
  seens   = {}
  who = ""
  sentence = ""
  who_working = false

  last_saids = {}

  trivData = {}
  isTriv = false

  trev_running = false
  trev_answers_running = false
  trev_guesses = {}
  real_answer = ""

  countdown_running = false

  configure do |c|
    c.server = "irc.libera.chat"
    c.user = "Monqui"
    c.password = "tbmsn"
    c.nick = "botqu9001"
    c.delay_joins = 15
    c.channels = ["#sconesandcream fatjoints", "#sconetv", "#bakedbeans"]
    c.timeouts.connect = 30
  end

  on :message, ".bots" do |m|
    m.reply "RePoRTiNg iN 🐵! [ruby] Lmao!"
  end

  on :message, /^!thank.*/ do |m|
    links = [
      "https://i.imgur.com/P2jEPy7.png", 
      "https://i.imgur.com/q1IzteE.jpg", 
      "https://i.imgur.com/BM1V3Um.png", 
      "https://i.imgur.com/RuLveTf.jpg", 
      "https://i.imgur.com/ramcAYX.png"
    ]
    m.reply links.sample
  end

  on :message, "!nonce" do |m|
    m.reply "French people."
  end

  on :message, "!nipples" do |m|
    m.reply "(.)(.) ooooo"
  end

  on :message, "!rub" do |m|
    m.reply "/me mmmmmmmmmmmmmmmm's"
  end

  on :message, "!quote" do |m|
	q = Quoter.new()
	m.reply(q.get_quote)
  end

  on :message, /^!quote (.+)$/ do |m, quote|
	q = Quoter.new()
	q.save_quote(quote)
	m.reply "K."
  end

  on :message, /^!delquote (.*)$/ do |m, id|
	q = Quoter.new()
	m.reply(q.del_quote(id))
  end

  on :channel, /^!apod$/ do |m|
    doc = Nokogiri::HTML.parse(URI.open('https://apod.nasa.gov/apod/astropix.html'))

    img = doc.xpath("/html/body/center[1]/p[2]/a/img")

    uri = "https://apod.nasa.gov/" + img.attr("src")
    name = "\u0002" + doc.xpath("/html/body/center[2]/b[1]").inner_html.strip + "\u000F"

    m.reply name + ": " + uri
  end

  on :channel, /^!moon$/ do |m|
    doc = Nokogiri::HTML.parse(URI.open('https://www.timeanddate.com/moon/phases/'))

    img = doc.xpath("//*[@id='cur-moon-percent']")

    m.reply img.inner_html.to_s
  end

  on :channel, /^!google (.*)$/ do |m, terms|
     google_url = "https://www.google.com/search?q=" + terms.gsub(' ', '%20')
     m.reply google_url
  end

  on :message, /([\.\d]+)lb/i do |m, val|
    if m.user.nick.to_s != " Ballbag" then
      m.reply val.to_s + "lb = " + (val.to_f * 0.454).round(2).to_s + "kg"
    end
  end

  on :message, /([\.\d]+)kg/i do |m, val|
    if m.user.nick.to_s != "Ballbag" then
      m.reply val.to_s + "kg = " + (val.to_f * 2.2046).round(2).to_s + "lb"
    end
  end

  on :message, /^!roll ([0-9]+)d([0-9]+)$/ do |m, num, max|
    if num.to_i > 30 or max.to_i > 100 then
      m.reply "bro be real."
      return
    end
   
    count = 0

    value = 0

    vals = []

    while count < num.to_i do
      roll = rand(1..max.to_i)
      vals.push(roll)
      value = value + roll
      count = count + 1
    end

    m.reply "#{m.user.nick} rolled " + vals.join('+') + " = " + value.to_s
  end

  on :message, /.* bear .*/ do |m|
    m.reply "https://33.media.tumblr.com/fe1446dc5f065b3685fe63c51121fc4f/tumblr_mhhadl964P1rgfo8qo1_r2_400.gif"
  end

  on :message, /.*trinity.*/i do |m|
    m.reply "https://tenor.com/view/trinity-taylor-drag-race-sip-cocktail-drama-gif-9086550"
  end

  on :message, "hello" do |m|
    m.reply "Hello, #{m.user.nick}"
  end

  on :channel, /^!pounce (\S*) (.*)$/ do |m, t, message|
    save_msg = {"source" => m.user.nick, "target" => t, "message" => '', 'time' => ''}

    if message.index("|") != nil then
      msg_parts = message.split(' | ')

      save_msg["message"] = msg_parts[1]

      triggerTime = Chronic.parse(msg_parts[0])

      if triggerTime == nil then
        save_msg["time"] = Time.now
      else
        save_msg["time"] = triggerTime
      end
    else
      save_msg["time"] = Time.now
      save_msg["message"] = message
    end

#    m.reply save_msg.inspect

    pounces.push save_msg

#    if !pounces[t.downcase].key?(m.user.nick) then
#      pounces[t.downcase][m.user.nick] = save_msg
#    else
#      pounces[t.downcase][m.user.nick] = pounces[t.downcase][m.user.nick] + " | " + message
#    end

    m.reply "Will pounce " + t + " next time I see 'em."
  end

  on :channel, /^(.*)$/ do |m, val|
    nick = m.user.nick

    now = Time.now

    remaining_pounces = []

    out_msgs = {}

    pounces.each do |p|
      if nick == p["target"] and now > p["time"] then
        out_msgs[nick] = p["source"] + " says " + p["message"] + "| " + out_msgs[nick].to_s
      else
        remaining_pounces.push p
      end
    end

    out_msgs.each do |key, val|
      m.reply "Hey " + key + ", " + val
    end
#    m.reply out_msgs.inspect

    pounces = remaining_pounces
    
#    if pounces.key?(nick.downcase) then
#      pounces[nick.downcase].each_key{|i|
#        m.reply "Hey " + nick + ", " + i.to_s + " says " + pounces[nick.downcase][i]
#      }
#      pounces.delete(nick.downcase)
  end

  on :message, /^!print (.*)$/ do |m, cmd|
    m.reply cmd
  end

  on :channel, /^.*$/ do |m|
    if m.message.slice(0, 2) != "s/" and m.message.slice(0, 1) != "!" and m.user.nick.to_s != "Ballbag" then
      last_saids[m.user.nick] = m.message
    end 
  end

  on :channel, /^!britify(.*)$/ do |m, msg|
    britese = ['oi', 'u wot', 'm8', 'mate', 'innit', 'bruv', 'fam', "ya kna wha 'm sayin"]
    msg = msg.strip
    
    orig = '?'

    if msg == '' then
      orig = last_saids[m.user.nick]
    else
      orig = msg
    end

    m.reply britese.sample + " " + 
      orig.gsub(/(o)/i, 'ou').
      gsub(/(z)/i, 's').
      gsub(/(ck)/i, 'que').
      gsub(/( of)/i, 'o\'').
      gsub(/(au)/i, 'o').
      gsub(/(and)/i, 'an\'').
      gsub(/(the)/i, 'tha').
      gsub(/(he)/i, '\'e') +
      " " + britese.sample
  end
#  on :channel, /^s\/(.*)\/(.*)\/? ?(.*) ?$/ do |m, f, r, n|

  on :channel, /^s\/(.*)\/(.*)(\/[^\s]*)?(\s+([^\s]+))?\s*$/ do |m, f, r, o, n, x|
    nick = m.user.nick

    if n != nil and n.strip != "" then
      nick = n.strip
    end

    ls = last_saids[nick]
    re = /#{f}/
    sedres = ls.gsub(re, r)
    m.reply nick + " meant to say: " + sedres #s.try(last_saids[nick])
  end

  on :channel, /^!fc (.*)$/ do |m, val|
    if m.user.nick.to_s != "Ballbag" then
      m.reply ((val.to_f-32).to_f/1.8).to_s + "C"
    end
  end

  on :channel, /^!cf (.*)$/ do |m, val|
    if m.user.nick.to_s != "Ballbag" then
      m.reply ((val.to_f * 1.8)+32).to_s + "F"
    end
  end

  on :message, /(^|\s)(-?\d+)f($|\s)/i do |m, junk, val, other|
    if m.user.nick.to_s != "Ballbag" then
      m.reply val.to_s + "F = " + ((val.to_i-32).to_f/1.8).round(2).to_s + "C"
    end
  end

  on :message, /(^|\s)(-?\d+)c($|\s)/i do |m, junk, val, other|
    if m.user.nick.to_s != "Ballbag" then
      m.reply val.to_s + "c = " + ((val.to_i * 1.8)+32).round(2).to_s + "F"
    end
  end

  on :channel, /^!triv$/ do |m|
    if isTriv == false then
      tm = TriviaMaster.new()
      trivData = tm.get_question()
#      m.reply questionBits.inspect
      isTriv = true
    end

    m.reply trivData['question'] + "  " + trivData['answer_mask']
  end

  on :channel, /^!trivstop$/ do |m|
    isTriv = false
    m.reply "Stopping trivia."
  end

  on :channel, /^!trivhint$/ do |m|
    new_mask = trivData['answer_mask']

    starPositions = []

    for i in 0 .. new_mask.length-1
      if new_mask[i] == '*' then starPositions.push(i) end
    end

    if starPositions.length == 0 and isTriv == true then
      isTriv = false
      m.reply "HOLY TITS botqui WON"
        if !data['trivias'].key?('botqui') then
          data['trivias']['botqui'] = 0
        end
        data['trivias'][nick] = data['trivias'][nick] + 1
        File.open("data.bd", 'w') { |file| file.write(JSON.generate(data)) }
        m.reply "botqui currently has " + data['trivias']['botqui'].to_s + " wins."
    else
      revealPos = starPositions.sample

      new_mask[revealPos] = trivData['answer'][revealPos]

      trivData['answer_mask'] = new_mask

      m.reply trivData['answer_mask']
    end
  end

  on :channel, /^(.*)$/ do |m, val|
    if isTriv then
      val = val.downcase
      if val.downcase == trivData['answer'].downcase then
        m.reply "YOU WON HOLY SHIT - the answer was " + trivData['answer']
        isTriv = false
        
        nick = m.user.nick

        if !data['trivias'].key?(nick) then
          data['trivias'][nick] = 0
        end

        data['trivias'][nick] = data['trivias'][nick] + 1
        File.open("data.bd", 'w') { |file| file.write(JSON.generate(data)) }
        m.reply nick + " currently has " + data['trivias'][nick].to_s + " wins."
      else
        tmp_answer = trivData['answer'].downcase
        new_mask = trivData['answer_mask']
    
        found = false
     
        for i in 0 .. [val.length, trivData['answer'].length].min
          if val[i] == tmp_answer[i] and trivData['answer_mask'][i] == '*' then
            found = true
            new_mask[i] = trivData['answer'][i]
          end
        end
        
        trivData['answer_mask'] = new_mask
        
        if found then
          m.reply new_mask + " (!trivstop to stop)"
        end
      end
    end
  end

  on :channel, /^!imgur (.*)$/ do |m, tag|
    m.reply "http://i.imgur.com/" + tag
  end

  on :channel, /^(.*)$/ do |m, last_said|
    seens[m.user.nick.downcase] = {"message": last_said, "time": Time.now().to_i}
  end

  on :channel, /^!seen (.*$)/ do |m, nick_long|
    nick = nick_long.strip
    if !seens.key?(nick.downcase) then
      m.reply "Haven't seen " + nick + " since last reboot..."
    else
      ht = human_time(Time.now().to_i - seens[nick.downcase][:time])
      m.reply nick + " said \"" + seens[nick.downcase][:message] + "\" " + ht + " ago."
    end
  end

  on :channel, /^!remind (.*)[\|:](.*)$/ do |m, time, event|
    event = event.strip
    triggerTime = Chronic.parse(time)

    if triggerTime == nil then 
      m.reply "Iunno wtf time that is."
      return
    else
      timeOut = triggerTime.to_i - Time.now().to_i
      
      if timeOut <= 0 then
        m.reply "YOU CAN'T CHANGE THE PAST, MAN!"
      else
        m.reply 'Will remind you to ' + event + ' in about ' + (timeOut).to_s + " secondsish."

        sleep(timeOut)

        m.reply m.user.nick.to_s + ": " + event
      end
    end
  end

  on :channel, /^!countdown$/ do |m|
    if countdown_running == false then
      countdown_running = true
      start = 5
      while start > 0 do
        m.reply start
        start = start - 1
        sleep(1)
      end
      countdown_running = false
      m.reply "GO!"
    else
      return
    end
  end 

  on :channel, /^!cd$/ do |m|
    if countdown_running == false then
      countdown_running = true
      start = 5
      while start > 0 do
        m.reply start
        start = start - 1
        sleep(1)
      end
      countdown_running = false
      m.reply "GO!"
    end
  end

  on :channel, /^!trev$/ do |m|
    if trev_running == true then
      m.reply "PLAY THE GAME!"
    else
      trev_running = true
      res = Net::HTTP.get_response(URI('https://opentdb.com/api.php?amount=1'))

      if res.is_a?(Net::HTTPSuccess)
        question = JSON.parse(res.body)
        question_text = HTMLEntities.new.decode(question["results"][0]["question"])
        answer = question["results"][0]["correct_answer"]
        category = question["results"][0]["category"]
        difficulty = question["results"][0]["difficulty"]
        
        multiplier = 1
        difficulty_tag = "H"
        case difficulty
          when "easy"
            multiplier = 0.33
            difficulty_tag = "E"
          when "medium"
            multiplier = 0.66
            difficulty_tag = "M"
          when "hard"
            multiplier = 1.0
            difficulty_tag = "H"
          else
            m.reply "wtf does " + difficulty + " mean??"
        end

        answers = question["results"][0]["incorrect_answers"]
        answers.push(answer)

        answers.shuffle!

        m.reply "🖕👁️👄👁️🖕"

        m.reply "[" + category + "] (\x02" + difficulty_tag + "\x02) " + question_text

        out_str = ""
        
        current_answer = "A"

        max_answer = answers.max{|c1, c2| HTMLEntities.new.decode(c1).length <=> HTMLEntities.new.decode(c2).length}
        max_length = max_answer.length + 2 # 2 for padding a bit...

        padded_answers = answers.map{|v| v.ljust(max_length.to_i) }

        answers_shown = 0

        padded_answers.each do |a|
          answers_shown = answers_shown + 1

          if a.strip == answer.strip then
            real_answer = current_answer.clone
          end

          out_str = out_str + "\x02" + current_answer + "\x0F) " + HTMLEntities.new.decode(a) + " "

          if answers_shown % 2 == 0 then
            out_str.strip!
            out_str = out_str + "\n"
          end

          current_answer.next!
        end

        m.reply out_str.strip

        m.reply "TRIVIA STARTING IN 5 SECONDS!"
        sleep(5)
        
        trev_answers_running = true
        m.reply "You have 30 seconds! GO!"
        start_time = Time.now.to_i
        sleep(30)

        m.reply "The answer was \x02" + real_answer + "\x0F) " + HTMLEntities.new.decode(answer) + "!"

        someone_won = false

        data = get_data('trevias')

        trev_guesses.each do |g|
          puts g.inspect
          puts trev_guesses.inspect
          if g[1] > 0 then
            someone_won = true
            nick = g[0]

            time_took = g[1] - start_time
            points = ((((30-time_took).to_f/30.0) * 100)*multiplier).floor.to_i

            if !data.key?(nick) then
              data[nick] = 0
            end

            data[nick] = data[nick] + points 

            m.reply nick + " got it and took " + time_took.to_s + "s!  Wins " + points.to_s + " points!  They have " + data[nick].to_s + " total points now!"
          end
        end         
        if someone_won == false  then
          m.reply "Looks like no one got it!  Better luck next time!"
        end

        set_data('trevias', data)

        trev_guesses = {}
        trev_running = false
        trev_answers_running = false
      else
        trev_running = false
        trev_answers_running = false #just in case
        m.reply "idk I failed to work I am sorry :("
      end
    end
  end

  on :channel, /^([abcd])$/i do |m, a|
    if trev_answers_running == true then
      nick = m.user.nick
      # check if nick exists in hash... if not add it.
      if trev_guesses.has_key? nick then
#        m.reply "lmao u already guessed!"
      else
        if real_answer.downcase == a.downcase then
          trev_guesses[nick] = Time.now.to_i
        else
          trev_guesses[nick] = 0
        end
        m.reply nick +  " is locked in with " + a + "!"
      end
    end
  end

  on :channel, /^!trevscores$/ do |m|
    scores = get_data('trevias')
    scores = scores.sort_by { |name, s| s}

    scores_str = ""
    
    real_scores = []

    scores.each do |e|
      real_scores.unshift e
    end

    real_scores.each do |e|
      scores_str = scores_str + "(" + fix_nick(e[0]) + ": " + e[1].to_s + ") "
    end

    m.reply scores_str
  end

  on :channel, /^!smoke$/ do |m|
    m.reply "                    (  )/"
    m.reply "                    )(/"
    m.reply "_________________  ( /)"
    m.reply "()__)____________)))))"   
  end
end

bot.start

