try: from collections import Counter
except: from Counter import Counter # For Python < 2.7
import traceback
import wordList
import anagram
import random
import math
import re

# Allows channel members to play an acronym-based word game.
# !jumble [min [max]] -- starts a new round, optionally setting the min and max length of words.
# !jumble end -- stops the current game.
# <guess> -- users must unjumble the given word and present their solution as a single word.
# !unjumble <word> -- lists all the acronyms of the given word.

# The possible words which may be used are taken from words.txt, and, if plugins.log has a log for
# the channel given by VOCAB_CHANNEL, tailored to match the vocabulary of that channel.
VOCAB_CHANNEL = '#calculus'

# If plugins.define.define is loaded, the definitions of any mentioned words will also be given.

words = set(wordList.fromFile('plugins/jumble/words.txt'))
logged = nicks = None
try:
    logged = Counter(wordList.fromLog(VOCAB_CHANNEL, 'plugins/log/database'))
    nicks = set(wordList.nicksFromLog(VOCAB_CHANNEL, 'plugins/log/database'))
    words |= set(w for w in logged if logged[w] > 99) - nicks # Include nonnicks logged > 99 times.
    anagrams = anagram.Finder(words)
    words &= set(w for w in logged if logged[w] > 1) # Exclude words logged < 2 times.
except Exception as e:
    anagrams = anagram.Finder(words)
    traceback.print_exc(e)
finally: 
    del logged, nicks

games = {}


def chmsg(event, server):
    chan, nick, msg = event['channel'], event['nicka'], event['msg']
    def respond(msg): server.send_msg(chan, wrap(msg, server))
    if chan in games: jumble = games[chan]
    else: jumble = games[chan] = Jumble()
    return jumble.said(server, msg, nick, respond)

def wrap(msg, server):
    max = server.MAX_SIZE - 100
    end = ' (...)'
    if len(msg) > max: return msg[0:max-len(end)] + end
    else: return msg

def define(server, word):
    if 'plugins.define.define' not in server.mod.modules: return None
    define = server.mod.modules['plugins.define.define']
    try: return define.instance.define(word)
    except IOError: pass
    except define.dictionary.ProtocolError: pass

class Jumble(object):
    __slots__ = 'word', 'solutions', 'min', 'max'
    
    def __init__(self):
        self.min = self.max = 0
        self.word = None
    
    def said(self, server, msg, nick, respond):
        match = re.match(r'!jumble(\s+\d+)?(\s+\d+)?\s*$', msg, re.I)
        if match: return self.call(server, respond, *match.groups())
        
        match = re.match(r'!jumble\s+end\s*$', msg, re.I)
        if match: return self.end(server, respond, *match.groups())
        
        match = re.match(r'!jumble\s.', msg, re.I)
        if match: return self.help(server, respond, *match.groups())
        
        match = re.match(r'!unjumble\s+([a-z]+)\s*$', msg, re.I)
        if match: return self.unjumble(server, respond, nick, *match.groups())
        
        match = re.match(r'\s*([a-z]+)\s*$', msg, re.I)
        if match: return self.guess(server, respond, nick, *match.groups())

    def call(self, server, respond, min, max):
        if min != None: self.min = int(min)
        if max != None: self.max = int(max)
        self.end(server, respond)
        self.start(server, respond)

    def start(self, server, respond):
        sub = set(w for w in words if self.min <= len(w) and (self.max == 0 or len(w) <= self.max))
        while len(sub):
            word = random.sample(sub, 1)[0]
            solutions = anagrams(word)
            if math.factorial(len(word)) != len(solutions):
                self.word, self.solutions = ''.join(chars), solutions
                return respond(self.word)
            else: sub.remove(word) # Every permutation of this word is in the dictionary.
        else: return respond("Failed to generate a word.") # There are no suitable words.

    def guess(self, server, respond, nick, word):
        if not self.word: return
        word = word.upper()
        if word in self.solutions:
            defn = define(server, word)
            if defn: respond("%s wins with %s (%s)" % (nick, word, defn))
            else: respond("%s wins with %s." % (nick, word))
            self.start(server, respond)

    def end(self, server, respond):
        if not self.word: return
        self.word = None
        if len(self.solutions) == 1:
            defn = define(server, tuple(self.solutions)[0])
            if defn: return respond('Nobody wins. Solutions: %s (%s)' %
                (', '.join(self.solutions), defn))
        return respond('Nobody wins. Solutions: %s.' % ', '.join(self.solutions))
    
    def unjumble(self, server, respond, nick, word):
        words = anagrams(word)
        if len(words): return respond(', '.join(words))
        return respond('No results.')
    
    def help(self, server, respond):
        respond('Available commands:'
            ' \002!jumble [min [max]]\002,'
            ' \002!jumble end\002,'
            ' \002!jumble help\002,'
            ' \002!unjumble <word>\002.')
                chars = list(word)
                while ''.join(chars) in solutions: random.shuffle(chars)