import logging
import logging.config
import re
from string import Template
from copy import copy
from datetime import time, timedelta, datetime

from PyQt5 import QtCore, QtGui, QtWidgets

import ostools
import parsetools
from mood import Mood
from dataobjs import PesterProfile, PesterHistory
from generic import PesterIcon, RightClickList, mysteryTime
from convo import PesterConvo, PesterInput, PesterText, PesterTabWindow
from parsetools import convertTags, addTimeInitial, timeProtocol, \
    lexMessage, colorBegin, colorEnd, mecmd, smiledict
from logviewer import PesterLogViewer

_datadir = ostools.getDataDir()
logging.config.fileConfig(_datadir + "logging.ini")
PchumLog = logging.getLogger('pchumLogger')

# Python 3
QString = str

def delta2txt(d, format="pc"):
    if type(d) is mysteryTime:
        return "?"
    if format == "pc":
        sign = "+" if d >= timedelta(0) else "-"
    else:
        if d == timedelta(0):
            return "i"
        sign = "F" if d >= timedelta(0) else "P"
    d = abs(d)
    totalminutes = (d.days*86400 + d.seconds) // 60
    hours = totalminutes // 60
    leftovermins = totalminutes % 60
    if hours < 100:
        if format == "pc":
            return "%s%d:%02d" % (sign, hours, leftovermins)
        else:
            return "%s%02d:%02d" % (sign, hours, leftovermins)
    else:
        if format == "pc":
            return "%s%d" % (sign, hours)
        else:
            return "%s%02d:%02d" % (sign, hours, leftovermins)

def txt2delta(txt):
    sign = 1
    if txt[0] == '?':
        return mysteryTime()
    if txt[0] == '+':
        txt = txt[1:]
    elif txt[0] == '-':
        sign = -1
        txt = txt[1:]
    l = txt.split(":")
    try:
        h = int(l[0])
        m = 0
        if len(l) > 1:
            m = int(l[1])
        timed = timedelta(0, h*3600+m*60)
    except ValueError:
        timed = timedelta(0)
    except OverflowError:
        if sign < 0:
            return timedelta(min)
        else:
            return timedelta(max)
    return sign*timed

def pcfGrammar(td):
    if td == timedelta(microseconds=1): # Replacement for mysteryTime </3
        when = "???"
        temporal = "???"
        pcf = "?"
    elif td > timedelta(0):
        when = "FROM NOW"
        temporal = "FUTURE"
        pcf = "F"
    elif td < timedelta(0):
        when = "AGO"
        temporal = "PAST"
        pcf = "P"
    else:
        when = "RIGHT NOW"
        temporal = "CURRENT"
        pcf = "C"
    return (temporal, pcf, when)

class TimeGrammar(object):
    def __init__(self, temporal, pcf, when, number="0"):
        self.temporal = temporal
        self.pcf = pcf
        self.when = when
        if number == "0" or number == 0:
            self.number = ""
        else:
            self.number = str(number)

class TimeTracker(list):
    def __init__(self, time=None):
        # mysteryTime breaks stuff now, so, uh
        # I'm replacing it with 1 day...
        if type(time)==mysteryTime:
            time = timedelta(microseconds=1)
        self.timerecord = {"P": [], "F": []}
        self.open = {}
        if time is not None:
            self.append(time)
            self.current=0
            self.addRecord(time)
            self.open[time] = False
        else:
            self.current=-1
    def addTime(self, timed):
        # mysteryTime </3
        if type(timed)==mysteryTime:
            timed = timedelta(microseconds=1)
        try:
            i = self.index(timed)
            self.current = i
            return True
        except ValueError:
            self.current = len(self)
            self.append(timed)
            self.open[timed] = False
            self.addRecord(timed)
            return False
    def prevTime(self):
        i = self.current
        i = (i - 1) % len(self)
        return self[i]
    def nextTime(self):
        i = self.current
        i = (i + 1) % len(self)
        return self[i]
    def setCurrent(self, timed):
        self.current = self.index(timed)
    def addRecord(self, timed):
        try:
            (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
        except TypeError:
            (temporal, pcf, when) = pcfGrammar(mysteryTime())
        if pcf == "C" or pcf == "?":
            return
        if timed in self.timerecord[pcf]:
            return
        self.timerecord[pcf].append(timed)
    def getRecord(self, timed):
        try:
            (temporal, pcf, when) = pcfGrammar(timed - timedelta(0))
        except TypeError:
            (temporal, pcf, when) = pcfGrammar(mysteryTime())
        if pcf == "C" or pcf == "?":
            return 0
        if len(self.timerecord[pcf]) > 1:
            return self.timerecord[pcf].index(timed)+1
        else:
            return 0
    def removeTime(self, timed):
        try:
            self.pop(self.index(timed))
            self.current = len(self)-1
            del self.open[timed]
            return True
        except ValueError:
            return None
    def openTime(self, time):
        if time in self.open:
            self.open[time] = True
    def openCurrentTime(self):
        timed = self.getTime()
        self.openTime(timed)
    def isFirstTime(self):
        timed = self.getTime()
        return not self.open[timed]
    def getTime(self):
        if self.current >= 0:
            return self[self.current]
        else:
            return None
    def getGrammar(self):
        timed = self.getTime()
        return self.getGrammarTime(timed)
    def getGrammarTime(self, timed):
        mytime = timedelta(0)
        try:
            (temporal, pcf, when) = pcfGrammar(timed - mytime)
        except TypeError:
            (temporal, pcf, when) = pcfGrammar(mysteryTime())
        if timed == mytime:
            return TimeGrammar(temporal, pcf, when, 0)
        return TimeGrammar(temporal, pcf, when, self.getRecord(timed))

class TimeInput(QtWidgets.QLineEdit):
    def __init__(self, timeslider, parent):
        super(TimeInput, self).__init__(parent)
        self.timeslider = timeslider
        self.setText("+0:00")
        self.timeslider.valueChanged[int].connect(self.setTime)
        self.editingFinished.connect(self.setSlider)
    @QtCore.pyqtSlot(int)
    def setTime(self, sliderval):
        self.setText(self.timeslider.getTime())
    @QtCore.pyqtSlot()
    def setSlider(self):
        value = str(self.text())
        timed = txt2delta(value)
        if type(timed) is mysteryTime:
            self.timeslider.setValue(0)
            self.setText("?")
            return
        sign = 1 if timed >= timedelta(0) else -1
        abstimed = abs(txt2delta(value))
        index = 50
        for i, td in enumerate(timedlist):
            if abstimed < td:
                index = i-1
                break
        self.timeslider.setValue(sign*index)
        text = delta2txt(timed)
        self.setText(text)

class TimeSlider(QtWidgets.QSlider):
    def __init__(self, orientation, parent):
        super(TimeSlider, self).__init__(orientation, parent)
        self.setTracking(True)
        self.setMinimum(-50)
        self.setMaximum(50)
        self.setValue(0)
        self.setPageStep(1)
    def getTime(self):
        time = timelist[abs(self.value())]
        sign = "+" if self.value() >= 0 else "-"
        return sign+time
    def mouseDoubleClickEvent(self, event):
        self.setValue(0)

class MemoTabWindow(PesterTabWindow):
    def __init__(self, mainwindow, parent=None):
        super(MemoTabWindow, self).__init__(mainwindow, parent, "memos")
    def addChat(self, convo):
        self.convos[convo.channel] = convo
        # either addTab or setCurrentIndex will trigger changed()
        newindex = self.tabs.addTab(convo.channel)
        self.tabIndices[convo.channel] = newindex
        self.tabs.setCurrentIndex(newindex)
        self.tabs.setTabIcon(newindex, PesterIcon(self.mainwindow.theme["memos/memoicon"]))
    def updateBlocked(self):
        pass
    def updateMood(self):
        pass

_ctag_begin = re.compile(r'<c=(.*?)>')

class MemoText(PesterText):
    def __init__(self, theme, parent=None):
        super(MemoText, self).__init__(theme, parent)
        if hasattr(self.parent(), 'mainwindow'):
            self.mainwindow = self.parent().mainwindow
        else:
            self.mainwindow = self.parent()
        if type(parent.parent()) is PesterTabWindow:
            self.tabobject = parent.parent()
            self.hasTabs = True
        else:
            self.hasTabs = False
        self.initTheme(theme)
        self.setReadOnly(True)
        self.setMouseTracking(True)
        self.textSelected = False
        self.copyAvailable[bool].connect(self.textReady)
        self.urls = {}
        for k in smiledict:
            self.addAnimation(QtCore.QUrl("smilies/%s" % (smiledict[k])), "smilies/%s" % (smiledict[k]))
        #self.mainwindow.animationSetting[bool].connect(self.animateChanged)

    def initTheme(self, theme):
        if "memos/scrollbar" in theme:
            self.setStyleSheet("QTextEdit { %s } QScrollBar:vertical { %s } QScrollBar::handle:vertical { %s } QScrollBar::add-line:vertical { %s } QScrollBar::sub-line:vertical { %s } QScrollBar:up-arrow:vertical { %s } QScrollBar:down-arrow:vertical { %s }" % (theme["memos/textarea/style"], theme["memos/scrollbar/style"], theme["memos/scrollbar/handle"], theme["memos/scrollbar/downarrow"], theme["memos/scrollbar/uparrow"], theme["memos/scrollbar/uarrowstyle"], theme["memos/scrollbar/darrowstyle"] ))
        else:
            self.setStyleSheet("QTextEdit { %s }" % theme["memos/textarea/style"])

        # So it doesn't inherit the memo's background image.
        # Fixes floating "PESTERLOG:"
        try:
            self.setStyleSheet(self.styleSheet() + " QMenu{" + theme["main/defaultwindow/style"] + "}")
        except:
            pass

    def addMessage(self, msg, chum):
        if type(msg) in [str, str]:
            lexmsg = lexMessage(msg)
        else:
            lexmsg = msg
        parent = self.parent()
        window = parent.mainwindow
        me = window.profile()
        if self.mainwindow.config.animations():
            for m in self.urls:
                if convertTags(lexmsg).find(self.urls[m].toString()) != -1:
                    if m.state() == QtGui.QMovie.NotRunning:
                        m.start()
        chumdb = window.chumdb
        if chum is not me: # SO MUCH WH1T3SP4C3 >:]
            if type(lexmsg[0]) is colorBegin: # get color tag
                colortag = lexmsg[0]
                try:
                    color = QtGui.QColor(*[int(c) for c in colortag.color.split(",")])
                except ValueError:
                    color = QtGui.QColor("black")
                else:
                    chumdb.setColor(chum.handle, color)
                    parent.updateColor(chum.handle, color)
            else:
                color = chumdb.getColor(chum.handle)
        else:
            color = me.color

        chum.color = color
        systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
        if chum is not me:
            if chum.handle in parent.times:
                time = parent.times[chum.handle]
                if time.getTime() is None:
                    # MY WAY OR THE HIGHWAY
                    time.addTime(timedelta(0))
            else:
                # new chum! time current
                newtime = timedelta(0)
                time = TimeTracker(newtime)
                parent.times[handle] = time
        else:
            time = parent.time

        if time.isFirstTime():
            grammar = time.getGrammar()
            joinmsg = chum.memojoinmsg(systemColor, time.getTime(), grammar, window.theme["convo/text/joinmemo"])
            self.append(convertTags(joinmsg))
            parent.mainwindow.chatlog.log(parent.channel, joinmsg)
            time.openCurrentTime()

        def makeSafe(msg):
            if msg.count("<c") > msg.count("</c>"):
                for i in range(msg.count("<c") - msg.count("</c>")):
                    msg = msg + "</c>"
            return "<span style=\"color:#000000\">" + msg + "</span>"
        if type(lexmsg[0]) is mecmd:
            memsg = chum.memsg(systemColor, lexmsg, time=time.getGrammar())
            window.chatlog.log(parent.channel, memsg)
            self.append(convertTags(memsg))
        else:
            self.append(makeSafe(convertTags(lexmsg)))
            window.chatlog.log(parent.channel, lexmsg)

    def changeTheme(self, theme):
        self.initTheme(theme)

class MemoInput(PesterInput):
    stylesheet_path = "memos/input/style"
    # karxi: Because of the use of stylesheet_path, we don't have to rewrite
    # this code.
    # Neat, huh?
    pass # So vim recognizes the end of this class

class PesterMemo(PesterConvo):
    # TODO: Clean up inheritance between these!! The inits are ugly.
    def __init__(self, channel, timestr, mainwindow, parent=None):
        QtWidgets.QFrame.__init__(self, parent)
        self.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
        self.channel = channel
        self.setObjectName(self.channel)
        self.mainwindow = mainwindow
        self.time = TimeTracker(txt2delta(timestr))
        self.setWindowTitle(channel)
        self.channelLabel = QtWidgets.QLabel(self)
        self.channelLabel.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Expanding))

        self.textArea = MemoText(self.mainwindow.theme, self)
        self.textInput = MemoInput(self.mainwindow.theme, self)
        self.textInput.setFocus()

        self.miniUserlist = QtWidgets.QPushButton(">\n>", self)
        #self.miniUserlist.setStyleSheet("border:1px solid #a68168; border-width: 2px 0px 2px 2px; height: 90px; width: 10px; color: #cd8f9d; font-family: 'Arial'; background: white; margin-left: 2px;")
        self.miniUserlist.clicked.connect(self.toggleUserlist)


        self.userlist = RightClickList(self)
        self.userlist.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Expanding))
        self.userlist.optionsMenu = QtWidgets.QMenu(self)
        self.pesterChumAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/pester"], self)
        self.pesterChumAction.triggered.connect(self.newPesterSlot)
        self.addchumAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/addchum"], self)
        self.addchumAction.triggered.connect(self.addChumSlot)
        self.banuserAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/banuser"], self)
        self.banuserAction.triggered.connect(self.banSelectedUser)
        self.opAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/opuser"], self)
        self.opAction.triggered.connect(self.opSelectedUser)
        self.voiceAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/voiceuser"], self)
        self.voiceAction.triggered.connect(self.voiceSelectedUser)
        self.quirkDisableAction = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirkkill"], self)
        self.quirkDisableAction.triggered.connect(self.killQuirkUser)
        self.userlist.optionsMenu.addAction(self.pesterChumAction)
        self.userlist.optionsMenu.addAction(self.addchumAction)
        # ban & op list added if we are op

        self.optionsMenu = QtWidgets.QMenu(self)
        self.optionsMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) # So it doesn't inherit the memo's background image.
                                                                                          # Fixes floating "PESTERLOG:"
        self.oocToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/ooc"], self)
        self.oocToggle.setCheckable(True)
        self.oocToggle.toggled[bool].connect(self.toggleOOC)
        self.quirksOff = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/quirksoff"], self)
        self.quirksOff.setCheckable(True)
        self.quirksOff.toggled[bool].connect(self.toggleQuirks)
        self.logchum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/viewlog"], self)
        self.logchum.triggered.connect(self.openChumLogs)
        self.invitechum = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/invitechum"], self)
        self.invitechum.triggered.connect(self.inviteChums)

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"):
        try:
            self._beepToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"], self)
        except:
            self._beepToggle = QtWidgets.QAction("BEEP ON MESSAGE", self)
        self._beepToggle.setCheckable(True)
        self._beepToggle.toggled[bool].connect(self.toggleBeep)

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"):
        try:
            self._flashToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"], self)
        except:
            self._flashToggle = QtWidgets.QAction("FLASH ON MESSAGE", self)
        self._flashToggle.setCheckable(True)
        self._flashToggle.toggled[bool].connect(self.toggleFlash)

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"):
        try:
            self._muteToggle = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"], self)
        except:
            self._muteToggle = QtWidgets.QAction("MUTE NOTIFICATIONS", self)
        self._muteToggle.setCheckable(True)
        self._muteToggle.toggled[bool].connect(self.toggleMute)

        self.optionsMenu.addAction(self.quirksOff)
        self.optionsMenu.addAction(self.oocToggle)

        self.optionsMenu.addAction(self._beepToggle)
        self.optionsMenu.addAction(self._flashToggle)
        self.optionsMenu.addAction(self._muteToggle)

        self.optionsMenu.addAction(self.logchum)
        self.optionsMenu.addAction(self.invitechum)

        self.chanModeMenu = QtWidgets.QMenu(self.mainwindow.theme["main/menus/rclickchumlist/memosetting"], self)
        self.chanNoquirks = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memonoquirk"], self)
        self.chanNoquirks.setCheckable(True)
        self.chanNoquirks.toggled[bool].connect(self.noquirksChan)
        self.chanHide = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memohidden"], self)
        self.chanHide.setCheckable(True)
        self.chanHide.toggled[bool].connect(self.hideChan)
        self.chanInvite = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memoinvite"], self)
        self.chanInvite.setCheckable(True)
        self.chanInvite.toggled[bool].connect(self.inviteChan)
        self.chanMod = QtWidgets.QAction(self.mainwindow.theme["main/menus/rclickchumlist/memomute"], self)
        self.chanMod.setCheckable(True)
        self.chanMod.toggled[bool].connect(self.modChan)
        self.chanModeMenu.addAction(self.chanNoquirks)
        self.chanModeMenu.addAction(self.chanHide)
        self.chanModeMenu.addAction(self.chanInvite)
        self.chanModeMenu.addAction(self.chanMod)
        self.chanModeMenu.setStyleSheet(self.mainwindow.theme["main/defaultwindow/style"]) # BWAH BWAH FLOATING "PESTERLOG:"

        self.timeslider = TimeSlider(QtCore.Qt.Horizontal, self)
        self.timeinput = TimeInput(self.timeslider, self)
        self.timeinput.setText(timestr)
        self.timeinput.setSlider()
        self.timetravel = QtWidgets.QPushButton("GO", self)
        self.timeclose = QtWidgets.QPushButton("CLOSE", self)
        self.timeswitchl = QtWidgets.QPushButton(self)
        self.timeswitchr = QtWidgets.QPushButton(self)

        self.timetravel.clicked.connect(self.sendtime)
        self.timeclose.clicked.connect(self.smashclock)
        self.timeswitchl.clicked.connect(self.prevtime)
        self.timeswitchr.clicked.connect(self.nexttime)

        self.times = {}

        self.initTheme(self.mainwindow.theme)

        # connect
        self.textInput.returnPressed.connect(self.sentMessage)

        layout_0 = QtWidgets.QVBoxLayout()
        layout_0.addWidget(self.textArea)
        layout_0.addWidget(self.textInput)

        layout_1 = QtWidgets.QHBoxLayout()
        layout_1.addLayout(layout_0)
        layout_1.addWidget(self.miniUserlist)
        layout_1.addWidget(self.userlist)

#        layout_1 = QtGui.QGridLayout()
#        layout_1.addWidget(self.timeslider, 0, 1, QtCore.Qt.AlignHCenter)
#        layout_1.addWidget(self.timeinput, 1, 0, 1, 3)
        layout_2 = QtWidgets.QHBoxLayout()
        layout_2.addWidget(self.timeslider)
        layout_2.addWidget(self.timeinput)
        layout_2.addWidget(self.timetravel)
        layout_2.addWidget(self.timeclose)
        layout_2.addWidget(self.timeswitchl)
        layout_2.addWidget(self.timeswitchr)
        self.layout = QtWidgets.QVBoxLayout()

        self.layout.addWidget(self.channelLabel)
        self.layout.addLayout(layout_1)
        self.layout.addLayout(layout_2)
        self.layout.setSpacing(0)
        margins = self.mainwindow.theme["memos/margins"]
        self.layout.setContentsMargins(margins["left"], margins["top"],
                                  margins["right"], margins["bottom"])

        self.setLayout(self.layout)

        if parent:
            parent.addChat(self)

        p = self.mainwindow.profile()
        timeGrammar = self.time.getGrammar()
        systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
        msg = p.memoopenmsg(systemColor, self.time.getTime(), timeGrammar, self.mainwindow.theme["convo/text/openmemo"], self.channel)
        self.time.openCurrentTime()
        self.textArea.append(convertTags(msg))
        self.mainwindow.chatlog.log(self.channel, msg)

        self.op = False
        self.newmessage = False
        self.history = PesterHistory()
        self.applyquirks = True
        self.ooc = False

        self.always_beep = False
        self.always_flash = False
        self.notifications_muted = False

    @QtCore.pyqtSlot()
    def toggleUserlist(self):
        if self.userlist.isHidden():
            self.userlist.show()
            self.miniUserlist.setText(">\n>")
            self.miniUserlist.setStyleSheet("%s border-width: 2px 0px 2px 2px;" % self.miniUserlist.styleSheet())
        else:
            self.userlist.hide()
            self.miniUserlist.setText("<\n<")
            self.miniUserlist.setStyleSheet("%s border-width: 2px;" % self.miniUserlist.styleSheet())

    def title(self):
        return self.channel
    def icon(self):
        return PesterIcon(self.mainwindow.theme["memos/memoicon"])

    def sendTimeInfo(self, newChum=False):
        if newChum:
            self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")+"i"),
                                  self.title())
        else:
            self.messageSent.emit("PESTERCHUM:TIME>%s" % (delta2txt(self.time.getTime(), "server")),
                                  self.title())

    def updateMood(self):
        pass
    def updateBlocked(self):
        pass
    def updateColor(self, handle, color):
        chums = self.userlist.findItems(handle, QtCore.Qt.MatchFlags(0))
        for c in chums:
            c.setForeground(QtGui.QBrush(color))
    def addMessage(self, text, handle):
        if type(handle) is bool:
            chum = self.mainwindow.profile()
        else:
            chum = PesterProfile(handle)
            self.notifyNewMessage()
        self.textArea.addMessage(text, chum)

    def initTheme(self, theme):
        self.resize(*theme["memos/size"])
        self.setStyleSheet("QtWidgets.QFrame { %s };" % (theme["memos/style"]))
        self.setWindowIcon(PesterIcon(theme["memos/memoicon"]))
        t = Template(theme["memos/label/text"])
        if self.mainwindow.advanced and hasattr(self, 'modes'):
            self.channelLabel.setText(t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes))
        else:
            self.channelLabel.setText(t.safe_substitute(channel=self.channel))
        self.channelLabel.setStyleSheet(theme["memos/label/style"])
        self.channelLabel.setAlignment(self.aligndict["h"][theme["memos/label/align/h"]] | self.aligndict["v"][theme["memos/label/align/v"]])
        self.channelLabel.setMaximumHeight(theme["memos/label/maxheight"])
        self.channelLabel.setMinimumHeight(theme["memos/label/minheight"])

        self.userlist.optionsMenu.setStyleSheet(theme["main/defaultwindow/style"])
        scrolls = "width: 12px; height: 12px; border: 0; padding: 0;"
        if "main/chums/scrollbar" in theme:
            self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["main/chums/scrollbar/style"] + scrolls, theme["main/chums/scrollbar/handle"], theme["main/chums/scrollbar/downarrow"], theme["main/chums/scrollbar/uparrow"], theme["main/chums/scrollbar/uarrowstyle"], theme["main/chums/scrollbar/darrowstyle"] ))
        elif "convo/scrollbar" in theme:
            self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s } QScrollBar::add-line { %s } QScrollBar::sub-line { %s } QScrollBar:up-arrow { %s } QScrollBar:down-arrow { %s }" % (theme["memos/userlist/style"], theme["convo/scrollbar/style"] + scrolls, theme["convo/scrollbar/handle"], "display:none;", "display:none;", "display:none;", "display:none;" ))
        else:
            self.userlist.setStyleSheet("QListWidget { %s } QScrollBar { %s } QScrollBar::handle { %s }" % (theme["memos/userlist/style"], scrolls, "background-color: black;"))
        self.userlist.setFixedWidth(theme["memos/userlist/width"])

        if self.userlist.isHidden():
            borders = "border-width: 2px;"
        else:
            borders = "border-width: 2px 0px 2px 2px;"
        self.miniUserlist.setStyleSheet("%s padding: 0px; margin: 0px; margin-left: 5px; width: 10px; height: 90px; %s" % (theme["memos/userlist/style"], borders))

        self.addchumAction.setText(theme["main/menus/rclickchumlist/addchum"])
        self.banuserAction.setText(theme["main/menus/rclickchumlist/banuser"])
        self.opAction.setText(theme["main/menus/rclickchumlist/opuser"])
        self.voiceAction.setText(theme["main/menus/rclickchumlist/voiceuser"])
        self.quirkDisableAction.setText(theme["main/menus/rclickchumlist/quirkkill"])
        self.quirksOff.setText(theme["main/menus/rclickchumlist/quirksoff"])
        self.logchum.setText(theme["main/menus/rclickchumlist/viewlog"])
        self.invitechum.setText(theme["main/menus/rclickchumlist/invitechum"])
        self.chanModeMenu.setTitle(theme["main/menus/rclickchumlist/memosetting"])
        self.chanNoquirks.setText(theme["main/menus/rclickchumlist/memonoquirk"])
        self.chanHide.setText(theme["main/menus/rclickchumlist/memohidden"])
        self.chanInvite.setText(theme["main/menus/rclickchumlist/memoinvite"])
        self.chanMod.setText(theme["main/menus/rclickchumlist/memomute"])

        self.timeinput.setFixedWidth(theme["memos/time/text/width"])
        self.timeinput.setStyleSheet(theme["memos/time/text/style"])
        slidercss = "QSlider { %s } QSlider::groove { %s } QSlider::handle { %s }" % (theme["memos/time/slider/style"], theme["memos/time/slider/groove"], theme["memos/time/slider/handle"])
        self.timeslider.setStyleSheet(slidercss)

        larrow = PesterIcon(self.mainwindow.theme["memos/time/arrows/left"])
        self.timeswitchl.setIcon(larrow)
        self.timeswitchl.setIconSize(larrow.realsize())
        self.timeswitchl.setStyleSheet(self.mainwindow.theme["memos/time/arrows/style"])
        self.timetravel.setStyleSheet(self.mainwindow.theme["memos/time/buttons/style"])
        self.timeclose.setStyleSheet(self.mainwindow.theme["memos/time/buttons/style"])

        rarrow = PesterIcon(self.mainwindow.theme["memos/time/arrows/right"])
        self.timeswitchr.setIcon(rarrow)
        self.timeswitchr.setIconSize(rarrow.realsize())
        self.timeswitchr.setStyleSheet(self.mainwindow.theme["memos/time/arrows/style"])

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/beeponmessage"):
        try:
            self._beepToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/beeponmessage"])
        except:
            self._beepToggle.setText("BEEP ON MESSAGE")

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/flashonmessage"):
        try:
            self._flashToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/flashonmessage"])
        except:
            self._flashToggle.setText("FLASH ON MESSAGE")

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/mutenotifications"):
        try:
            self._muteToggle.setText(self.mainwindow.theme["main/menus/rclickchumlist/mutenotifications"])
        except:
            self._muteToggle.setText("MUTE NOTIFICATIONS")

        #if self.mainwindow.theme.has_key("main/menus/rclickchumlist/pester"):
        try:
            self.pesterChumAction.setText(self.mainwindow.theme["main/menus/rclickchumlist/pester"])
        except:
            pass

    def changeTheme(self, theme):
        self.initTheme(theme)
        self.textArea.changeTheme(theme)
        self.textInput.changeTheme(theme)
        margins = theme["memos/margins"]
        self.layout.setContentsMargins(margins["left"], margins["top"],
                                  margins["right"], margins["bottom"])
        for item in [self.userlist.item(i) for i in range(0,self.userlist.count())]:
            self.iconCrap(item)

    def addUser(self, handle):
        chumdb = self.mainwindow.chumdb
        defaultcolor = QtGui.QColor("black")
        founder = False
        op =      False
        halfop =  False
        admin =   False
        voice =   False
        if handle[0] == '@':
            op = True
            handle = handle[1:]
            if handle == self.mainwindow.profile().handle:
                self.userlist.optionsMenu.addAction(self.opAction)
                self.userlist.optionsMenu.addAction(self.banuserAction)
                self.optionsMenu.addMenu(self.chanModeMenu)
                self.op = True
        elif handle[0] == '%':
            halfop = True
            handle = handle[1:]
            if handle == self.mainwindow.profile().handle:
                self.userlist.optionsMenu.addAction(self.opAction)
                self.userlist.optionsMenu.addAction(self.banuserAction)
                self.optionsMenu.addMenu(self.chanModeMenu)
                self.halfop = True
        elif handle[0] == '+':
            voice = True
            handle = handle[1:]
        elif handle[0] == '~':
            founder = True
            handle = handle[1:]
        elif handle[0] == '&':
            admin = True
            handle = handle[1:]
        item = QtWidgets.QListWidgetItem(handle)
        if handle == self.mainwindow.profile().handle:
            color = self.mainwindow.profile().color
        else:
            color = chumdb.getColor(handle, defaultcolor)
        item.box = (handle == "evacipatedBox")
        item.setForeground(QtGui.QBrush(color))
        item.founder = founder
        item.op = op
        item.halfop = halfop
        item.admin = admin
        item.voice = voice
        self.umodes = ["box", "founder", "admin", "op", "halfop", "voice"]
        self.iconCrap(item)
        self.userlist.addItem(item)
        self.sortUsers()

    def sortUsers(self):
        users = []
        listing = self.userlist.item(0)
        while listing is not None:
            users.append(self.userlist.takeItem(0))
            listing = self.userlist.item(0)
        users.sort(key=lambda x: ((-1 if x.box else (0 if x.founder else (1 if x.admin else (2 if x.op else (3 if x.halfop else (4 if x.voice else 5)))))), x.text()))
        for u in users:
            self.userlist.addItem(u)

    def updateChanModes(self, modes, op):
        if not hasattr(self, 'modes'): self.modes = ""
        chanmodes = list(str(self.modes))
        if chanmodes and chanmodes[0] == "+": chanmodes = chanmodes[1:]
        modes = str(modes)
        if op:
            systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
            chum = self.mainwindow.profile()
            opchum = PesterProfile(op)
            if op in self.times:
                opgrammar = self.times[op].getGrammar()
            elif op == self.mainwindow.profile().handle:
                opgrammar = self.time.getGrammar()
            else:
                opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
        if modes[0] == "+":
            for m in modes[1:]:
                if m not in chanmodes:
                    chanmodes.extend(m)
            # Make +c (disable ANSI colours) disable quirks.
            if modes.find("c") >= 0:
                self.chanNoquirks.setChecked(True)
                self.quirksOff.setChecked(True)
                self.applyquirks = False
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("s") >= 0:
                self.chanHide.setChecked(True)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("i") >= 0:
                self.chanInvite.setChecked(True)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("m") >= 0:
                self.chanMod.setChecked(True)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)

            #New
            if modes.find("C") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-CTCP", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("D") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Join Delayed", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("f") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Flood Protected", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("G") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Censored", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("H") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Remembering Chat History", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("k") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Key-only", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("K") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("L") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Redirecting", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("K") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("l") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Limiting maximum amount of users", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("M") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Non-Auth muted", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("N") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Handle-locked", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("O") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "An Oper-Only channel", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("P") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Permanent", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("Q") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-kick", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("R") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered users only", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("r") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("z") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secure-only", True)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
        elif modes[0] == "-":
            for i in modes[1:]:
                try:
                    chanmodes.remove(i)
                except ValueError:
                    pass
            if modes.find("c") >= 0:
                self.chanNoquirks.setChecked(False)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "A No-Quirk zone", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("s") >= 0:
                self.chanHide.setChecked(False)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secret", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("i") >= 0:
                self.chanInvite.setChecked(False)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Invite-Only", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("m") >= 0:
                self.chanMod.setChecked(False)
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Muted", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)

            #New
            if modes.find("C") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-CTCP", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("D") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Join Delayed", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("f") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Flood Protected", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("G") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Censored", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("H") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Remembering Chat History", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("k") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Key-only", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("K") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("L") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Redirecting", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("K") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-Knock", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("l") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Limiting maximum amount of users", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("M") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Non-Auth muted", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("N") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Handle-locked", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("O") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "An Oper-Only channel", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("P") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Permanent", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("Q") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "No-kick", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("R") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered users only", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("r") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Registered", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            if modes.find("z") >= 0:
                if op:
                    msg = chum.memomodemsg(opchum, opgrammar, systemColor, "Secure-only", False)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
        chanmodes.sort()
        self.modes = "+" + "".join(chanmodes)
        if self.mainwindow.advanced:
            t = Template(self.mainwindow.theme["memos/label/text"])
            self.channelLabel.setText(t.safe_substitute(channel=self.channel) + "(%s)" % (self.modes))

    def timeUpdate(self, handle, cmd):
        window = self.mainwindow
        chum = PesterProfile(handle)
        systemColor = QtGui.QColor(window.theme["memos/systemMsgColor"])
        close = None
        # old TC command?
        try:
            secs = int(cmd)
            time = datetime.fromtimestamp(secs)
            timed = time - datetime.now()
            s = (timed.seconds // 60)*60
            timed = timedelta(timed.days, s)
        except ValueError:
            try:
                if cmd == "i":
                    timed = timedelta(0)
                else:
                    if cmd[len(cmd)-1] == 'c':
                        close = timeProtocol(cmd)
                        timed = None
                    else:
                        timed = timeProtocol(cmd)
            except:
                PchumLog.warning("Invalid PESTERCHUM:TIME> " + str(cmd))
                timed = timedelta(0)

        if handle in self.times:
            if close is not None:
                if close in self.times[handle]:
                    self.times[handle].setCurrent(close)
                    grammar = self.times[handle].getGrammar()
                    self.times[handle].removeTime(close)
                    msg = chum.memoclosemsg(systemColor, grammar, window.theme["convo/text/closememo"])
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
            elif timed not in self.times[handle]:
                self.times[handle].addTime(timed)
            else:
                self.times[handle].setCurrent(timed)
        else:
            if timed is not None:
                ttracker = TimeTracker(timed)
                self.times[handle] = ttracker

    @QtCore.pyqtSlot()
    def sentMessage(self):
        text = str(self.textInput.text())
        
        return parsetools.kxhandleInput(self, text, flavor="memos")
        
    @QtCore.pyqtSlot(QString)
    def namesUpdated(self, channel):
        c = str(channel)
        if c.lower() != self.channel.lower(): return
        # get namesdb
        namesdb = self.mainwindow.namesdb
        # reload names
        self.userlist.clear()
        for n in self.mainwindow.namesdb[self.channel]:
            self.addUser(n)
    @QtCore.pyqtSlot(QString, QString)
    def modesUpdated(self, channel, modes):
        c = str(channel)
        if c.lower() == self.channel.lower():
            self.updateChanModes(modes, None)

    @QtCore.pyqtSlot(QString)
    def closeInviteOnly(self, channel):
        c = str(channel)
        if c.lower() == self.channel.lower():
            self.mainwindow.inviteOnlyChan['QString'].disconnect(self.closeInviteOnly)
            if self.parent():
                PchumLog.info(self.channel)
                i = self.parent().tabIndices[self.channel]
                self.parent().tabClose(i)
            else:
                self.close()
            msgbox = QtWidgets.QMessageBox()
            msgbox.setStyleSheet("QMessageBox{" + self.mainwindow.theme["main/defaultwindow/style"] + "}")
            msgbox.setText("%s: Invites only!" % (c))
            msgbox.setInformativeText("This channel is invite-only. You must get an invitation from someone on the inside before entering.")
            msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            ret = msgbox.exec_()

    def quirkDisable(self, op, msg):
        chums = self.userlist.findItems(op, QtCore.Qt.MatchFlags(0))
        for c in chums:
            if c.op:
                if msg == self.mainwindow.profile().handle:
                    self.quirksOff.setChecked(True)
                    self.applyquirks = False
                    systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
                    chum = self.mainwindow.profile()
                    opchum = PesterProfile(op)
                    if op in self.times:
                        opgrammar = self.times[op].getGrammar()
                    elif op == self.mainwindow.profile().handle:
                        opgrammar = self.time.getGrammar()
                    else:
                        opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
                    msg = chum.memoquirkkillmsg(opchum, opgrammar, systemColor)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)

    def chumOPstuff(self, h, op):
        chum = PesterProfile(h)
        if h == self.mainwindow.profile().handle:
            chum = self.mainwindow.profile()
            ttracker = self.time
            curtime = self.time.getTime()
        elif h in self.times:
            ttracker = self.times[h]
        else:
            ttracker = TimeTracker(timedelta(0))
        opchum = PesterProfile(op)
        PchumLog.debug("op = " + op)
        PchumLog.debug("opchum = " + opchum.handle)
        if op in self.times:
            opgrammar = self.times[op].getGrammar()
        elif op == self.mainwindow.profile().handle:
            opgrammar = self.time.getGrammar()
        else:
            opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
        return (chum, opchum, opgrammar)
    def iconCrap(self, c, down=True):
        for m in (self.umodes if down else reversed(self.umodes)):
            if eval("c."+m):
                if m == "box":
                    icon = PesterIcon("smilies/box.png")
                else:
                    icon = PesterIcon(self.mainwindow.theme["memos/"+m+"/icon"])
                c.setIcon(icon)
                return
        icon = QtGui.QIcon()
        c.setIcon(icon)

    @QtCore.pyqtSlot()
    def dumpNetsplit(self):
        if (len(self.netsplit) > 0):
            chum = self.mainwindow.profile()
            systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
            msg = chum.memonetsplitmsg(systemColor, self.netsplit)
            self.textArea.append(convertTags(msg))
            self.mainwindow.chatlog.log(self.channel, msg)
        del self.netsplit

    @QtCore.pyqtSlot(QString, QString, QString)
    def userPresentChange(self, handle, channel, update):
        h = str(handle)
        c = str(channel)
        update = str(update)
        PchumLog.debug("h=%s\nc=%s\nupdate=%s" % (h,c,update))
        if update[0:4] == "kick": # yeah, i'm lazy.
            l = update.split(":")
            update = l[0]
            op = l[1]
            reason = ":".join(l[2:])
        if update == "nick":
            l = h.split(":")
            oldnick = l[0]
            newnick = l[1]
            h = oldnick
        if update[0:1] in ["+", "-"]:
            l = update.split(":")
            update = l[0]
            op = l[1]
        if (update in ["join","left", "kick", \
                       "+q", "-q", "+o", "-o", "+h", "-h", \
                       "+a", "-a", "+v", "-v"]) \
                and c.lower() != self.channel.lower():
            return
        chums = self.userlist.findItems(h, QtCore.Qt.MatchFlags(0))
        systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
        # print exit
        if update in ("quit", "left", "nick", "netsplit"):
            if update == "netsplit":
                if not hasattr(self, "netsplit"):
                    self.netsplit = []
                    QtCore.QTimer.singleShot(1500, self, QtCore.SLOT('dumpNetsplit()'))
            for c in chums:
                chum = PesterProfile(h)
                self.userlist.takeItem(self.userlist.row(c))
                if h not in self.times:
                    self.times[h] = TimeTracker(timedelta(0))
                allinitials = []
                while self.times[h].getTime() is not None:
                    t = self.times[h]
                    grammar = t.getGrammar()
                    allinitials.append("%s%s%s" % (grammar.pcf, chum.initials(), grammar.number))
                    self.times[h].removeTime(t.getTime())
                if update == "netsplit":
                    self.netsplit.extend(allinitials)
                else:
                    msg = chum.memoclosemsg(systemColor, allinitials, self.mainwindow.theme["convo/text/closememo"])
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
                if update == "nick":
                    self.addUser(newnick)
                    newchums = self.userlist.findItems(newnick, QtCore.Qt.MatchFlags(0))
                    for nc in newchums:
                        for c in chums:
                            nc.founder = c.founder
                            nc.op      = c.op
                            nc.halfop  = c.halfop
                            nc.admin   = c.admin
                            self.iconCrap(nc)
                    self.sortUsers()
        elif update == "kick":
            if len(chums) == 0:
                return
            c = chums[0]
            chum = PesterProfile(h)
            if h == self.mainwindow.profile().handle:
                chum = self.mainwindow.profile()
                ttracker = self.time
                curtime = self.time.getTime()
            elif h in self.times:
                ttracker = self.times[h]
            else:
                ttracker = TimeTracker(timedelta(0))
            allinitials = []
            opchum = PesterProfile(op)
            if op in self.times:
                opgrammar = self.times[op].getGrammar()
            elif op == self.mainwindow.profile().handle:
                opgrammar = self.time.getGrammar()
            else:
                opgrammar = TimeGrammar("CURRENT", "C", "RIGHT NOW")
            while ttracker.getTime() is not None:
                grammar = ttracker.getGrammar()
                allinitials.append("%s%s%s" % (grammar.pcf, chum.initials(), grammar.number))
                ttracker.removeTime(ttracker.getTime())
            msg = chum.memobanmsg(opchum, opgrammar, systemColor, allinitials, reason)
            self.textArea.append(convertTags(msg))
            self.mainwindow.chatlog.log(self.channel, msg)

            if chum is self.mainwindow.profile():
                # are you next?
                msgbox = QtWidgets.QMessageBox()
                msgbox.setStyleSheet("QMessageBox{" + self.mainwindow.theme["main/defaultwindow/style"] + "}")
                msgbox.setText(self.mainwindow.theme["convo/text/kickedmemo"])

                # Add ban(kick) reason
                # l = split update
                kick_msg = ("press 0k to rec0nnect or cancel to absc0nd")
                if len(l) >= 3:
                    try:
                        if l[1] != l[2]: # If there's no reason then reason is set to handle
                            # Process spare ':' characters (this might not be safe?)
                            aggrievocation = l[1] + ": " + l[2]
                            if len(l) > 3:
                                aggrievocation += ':'
                                for x in range(3, len(l)):
                                    aggrievocation += l[x]
                                    # Not for last slice
                                    if x != (len(l)-1):
                                        aggrievocation += ':'
                            kick_msg = ("%s\n\npress 0k to rec0nnect or cancel to absc0nd" % aggrievocation)
                    except IndexError as e:
                        # This shouldn't happen
                        PchumLog.warning("kickmsg IndexError: %s" % e)
                msgbox.setInformativeText(kick_msg)
                msgbox.setStandardButtons(QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel)
                # Find the OK button and make it default
                for b in msgbox.buttons():
                    if msgbox.buttonRole(b) == QtWidgets.QMessageBox.AcceptRole:
                        # We found the 'OK' button, set it as the default
                        b.setDefault(True)
                        b.setAutoDefault(True)
                        # Actually set it as the selected option, since we're
                        # already stealing focus
                        b.setFocus()
                        break
                ret = msgbox.exec_()
                if ret == QtWidgets.QMessageBox.Ok:
                    self.userlist.clear()
                    self.time = TimeTracker(curtime)
                    self.resetSlider(curtime)
                    self.mainwindow.joinChannel.emit(self.channel)
                    me = self.mainwindow.profile()
                    self.time.openCurrentTime()
                    msg = me.memoopenmsg(systemColor, self.time.getTime(), self.time.getGrammar(), self.mainwindow.theme["convo/text/openmemo"], self.channel)
                    self.textArea.append(convertTags(msg))
                    self.mainwindow.chatlog.log(self.channel, msg)
                elif ret == QtWidgets.QMessageBox.Cancel:
                    if self.parent():
                        i = self.parent().tabIndices[self.channel]
                        self.parent().tabClose(i)
                    else:
                        self.close()
            else:
                # i warned you about those stairs bro
                self.userlist.takeItem(self.userlist.row(c))
        elif update == "join":
            self.addUser(h)
            time = self.time.getTime()
            serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server")
            self.messageSent.emit(serverText, self.title())
        elif update == "+q":
            for c in chums:
                c.founder = True
                self.iconCrap(c)
            self.sortUsers()
        elif update == "-q":
            for c in chums:
                c.founder = False
                self.iconCrap(c)
            self.sortUsers()
        elif update == "+o":
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                PchumLog.debug("chum.handle = %s\nopchum.handle = %s\nopgrammar = %s\n systemColor = %s\n" % (chum.handle, opchum.handle, opgrammar, systemColor))
                msg = chum.memoopmsg(opchum, opgrammar, systemColor)
                PchumLog.debug("post memoopmsg")
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.op = True
                self.iconCrap(c)
                if str(c.text()) == self.mainwindow.profile().handle:
                    self.userlist.optionsMenu.addAction(self.opAction)
                    self.userlist.optionsMenu.addAction(self.voiceAction)
                    self.userlist.optionsMenu.addAction(self.banuserAction)
                    self.userlist.optionsMenu.addAction(self.quirkDisableAction)
                    self.optionsMenu.addMenu(self.chanModeMenu)
            self.sortUsers()
        elif update == "-o":
            self.mainwindow.channelNames.emit(self.channel)
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                msg = chum.memodeopmsg(opchum, opgrammar, systemColor)
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.op = False
                self.iconCrap(c)
                if str(c.text()) == self.mainwindow.profile().handle:
                    self.userlist.optionsMenu.removeAction(self.opAction)
                    self.userlist.optionsMenu.removeAction(self.voiceAction)
                    self.userlist.optionsMenu.removeAction(self.banuserAction)
                    self.userlist.optionsMenu.removeAction(self.quirkDisableAction)
                    self.optionsMenu.removeAction(self.chanModeMenu.menuAction())
            self.sortUsers()
        elif update == "+h":
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                msg = chum.memoopmsg(opchum, opgrammar, systemColor)
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.halfop = True
                self.iconCrap(c)
                if str(c.text()) == self.mainwindow.profile().handle:
                    self.userlist.optionsMenu.addAction(self.opAction)
                    self.userlist.optionsMenu.addAction(self.voiceAction)
                    self.userlist.optionsMenu.addAction(self.banuserAction)
                    self.userlist.optionsMenu.addAction(self.quirkDisableAction)
                    self.optionsMenu.addMenu(self.chanModeMenu)
            self.sortUsers()
        elif update == "-h":
            self.mainwindow.channelNames.emit(self.channel)
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                msg = chum.memodeopmsg(opchum, opgrammar, systemColor)
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.halfop = False
                self.iconCrap(c)
                if str(c.text()) == self.mainwindow.profile().handle:
                    self.userlist.optionsMenu.removeAction(self.opAction)
                    self.userlist.optionsMenu.removeAction(self.voiceAction)
                    self.userlist.optionsMenu.removeAction(self.banuserAction)
                    self.userlist.optionsMenu.removeAction(self.quirkDisableAction)
                    self.optionsMenu.removeAction(self.chanModeMenu.menuAction())
            self.sortUsers()
        elif update == "+a":
            for c in chums:
                c.admin = True
                self.iconCrap(c)
            self.sortUsers()
        elif update == "-a":
            for c in chums:
                c.admin = False
                self.iconCrap(c)
            self.sortUsers()
        elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]:
            self.updateChanModes(update, op)
        elif update == "+v":
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                msg = chum.memovoicemsg(opchum, opgrammar, systemColor)
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.voice = True
                self.iconCrap(c)
            self.sortUsers()
        elif update == "-v":
            if self.mainwindow.config.opvoiceMessages():
                (chum, opchum, opgrammar) = self.chumOPstuff(h, op)
                msg = chum.memodevoicemsg(opchum, opgrammar, systemColor)
                self.textArea.append(convertTags(msg))
                self.mainwindow.chatlog.log(self.channel, msg)
            for c in chums:
                c.voice = False
                self.iconCrap(c)
            self.sortUsers()
        elif c.lower() == self.channel.lower() and h == "" and update[0] in ["+","-"]:
            self.updateChanModes(update, op)

    @QtCore.pyqtSlot()
    def newPesterSlot(self):
        # We're opening a pester with someone in our user list.
        user = self.userlist.currentItem()
        if not user:
            return
        user = str(user.text())
        self.mainwindow.newConversation(user)

    @QtCore.pyqtSlot()
    def addChumSlot(self):
        if not self.userlist.currentItem():
            return
        currentChum = PesterProfile(str(self.userlist.currentItem().text()))
        self.mainwindow.addChum(currentChum)
    @QtCore.pyqtSlot()
    def banSelectedUser(self):
        if not self.userlist.currentItem():
            return
        currentHandle = str(self.userlist.currentItem().text())
        (reason, ok) = QtWidgets.QInputDialog.getText(self, "Ban User", "Enter the reason you are banning this user (optional):")
        if ok:
            self.mainwindow.kickUser.emit("%s:%s" % (currentHandle, reason), self.channel)
    @QtCore.pyqtSlot()
    def opSelectedUser(self):
        if not self.userlist.currentItem():
            return
        currentHandle = str(self.userlist.currentItem().text())
        self.mainwindow.setChannelMode.emit(self.channel, "+o", currentHandle)
    @QtCore.pyqtSlot()
    def voiceSelectedUser(self):
        if not self.userlist.currentItem():
            return
        currentHandle = str(self.userlist.currentItem().text())
        self.mainwindow.setChannelMode.emit(self.channel, "+v", currentHandle)
    @QtCore.pyqtSlot()
    def killQuirkUser(self):
        if not self.userlist.currentItem():
            return
        currentHandle = str(self.userlist.currentItem().text())
        self.mainwindow.killSomeQuirks.emit(self.channel, currentHandle)

    def resetSlider(self, time, send=True):
        self.timeinput.setText(delta2txt(time))
        self.timeinput.setSlider()
        if send:
            self.sendtime()

    @QtCore.pyqtSlot()
    def openChumLogs(self):
        currentChum = self.channel
        self.mainwindow.chumList.pesterlogviewer = PesterLogViewer(currentChum, self.mainwindow.config, self.mainwindow.theme, self.mainwindow)
        self.mainwindow.chumList.pesterlogviewer.rejected.connect(self.mainwindow.chumList.closeActiveLog)
        self.mainwindow.chumList.pesterlogviewer.show()
        self.mainwindow.chumList.pesterlogviewer.raise_()
        self.mainwindow.chumList.pesterlogviewer.activateWindow()

    @QtCore.pyqtSlot()
    def inviteChums(self):
        if not hasattr(self, 'invitechums'):
            self.invitechums = None
        if not self.invitechums:
            (chum, ok) = QtWidgets.QInputDialog.getText(self, "Invite to Chat", "Enter the chumhandle of the user you'd like to invite:")
            if ok:
                chum = str(chum)
                self.mainwindow.inviteChum.emit(chum, self.channel)
            self.invitechums = None

    @QtCore.pyqtSlot(bool)
    def noquirksChan(self, on):
        x = ["-","+"][on]
        self.mainwindow.setChannelMode.emit(self.channel, x+"c", "")
    @QtCore.pyqtSlot(bool)
    def hideChan(self, on):
        x = ["-","+"][on]
        self.mainwindow.setChannelMode.emit(self.channel, x+"s", "")
    @QtCore.pyqtSlot(bool)
    def inviteChan(self, on):
        x = ["-","+"][on]
        self.mainwindow.setChannelMode.emit(self.channel, x+"i", "")
    @QtCore.pyqtSlot(bool)
    def modChan(self, on):
        x = ["-","+"][on]
        self.mainwindow.setChannelMode.emit(self.channel, x+"m", "")

    @QtCore.pyqtSlot()
    def sendtime(self):
        me = self.mainwindow.profile()
        systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
        time = txt2delta(self.timeinput.text())
        present = self.time.addTime(time)

        serverText = "PESTERCHUM:TIME>"+delta2txt(time, "server")
        self.messageSent.emit(serverText, self.title())
    @QtCore.pyqtSlot()
    def smashclock(self):
        me = self.mainwindow.profile()
        time = txt2delta(self.timeinput.text())
        removed = self.time.removeTime(time)
        if removed:
            grammar = self.time.getGrammarTime(time)
            systemColor = QtGui.QColor(self.mainwindow.theme["memos/systemMsgColor"])
            msg = me.memoclosemsg(systemColor, grammar, self.mainwindow.theme["convo/text/closememo"])
            self.textArea.append(convertTags(msg))
            self.mainwindow.chatlog.log(self.channel, msg)

        newtime = self.time.getTime()
        if newtime is None:
            newtime = timedelta(0)
            self.resetSlider(newtime, send=False)
        else:
            self.resetSlider(newtime)
    @QtCore.pyqtSlot()
    def prevtime(self):
        time = self.time.prevTime()
        self.time.setCurrent(time)
        self.resetSlider(time)
        self.textInput.setFocus()
    @QtCore.pyqtSlot()
    def nexttime(self):
        time = self.time.nextTime()
        self.time.setCurrent(time)
        self.resetSlider(time)
        self.textInput.setFocus()
    def closeEvent(self, event):
        self.mainwindow.waitingMessages.messageAnswered(self.channel)
        self.windowClosed.emit(self.title())

    windowClosed = QtCore.pyqtSignal('QString')


timelist = ["0:00", "0:01", "0:02", "0:04", "0:06", "0:10", "0:14", "0:22", "0:30", "0:41", "1:00", "1:34", "2:16", "3:14", "4:13", "4:20", "5:25", "6:12", "7:30", "8:44", "10:25", "11:34", "14:13", "16:12", "17:44", "22:22", "25:10", "33:33", "42:00", "43:14", "50:00", "62:12", "75:00", "88:44", "100", "133", "143", "188", "200", "222", "250", "314", "333", "413", "420", "500", "600", "612", "888", "1000", "1025"]

timedlist = [timedelta(0), timedelta(0, 60), timedelta(0, 120), timedelta(0, 240), timedelta(0, 360), timedelta(0, 600), timedelta(0, 840), timedelta(0, 1320), timedelta(0, 1800), timedelta(0, 2460), timedelta(0, 3600), timedelta(0, 5640), timedelta(0, 8160), timedelta(0, 11640), timedelta(0, 15180), timedelta(0, 15600), timedelta(0, 19500), timedelta(0, 22320), timedelta(0, 27000), timedelta(0, 31440), timedelta(0, 37500), timedelta(0, 41640), timedelta(0, 51180), timedelta(0, 58320), timedelta(0, 63840), timedelta(0, 80520), timedelta(1, 4200), timedelta(1, 34380), timedelta(1, 64800), timedelta(1, 69240), timedelta(2, 7200), timedelta(2, 51120), timedelta(3, 10800), timedelta(3, 60240), timedelta(4, 14400), timedelta(5, 46800), timedelta(5, 82800), timedelta(7, 72000), timedelta(8, 28800), timedelta(9, 21600), timedelta(10, 36000), timedelta(13, 7200), timedelta(13, 75600), timedelta(17, 18000), timedelta(17, 43200), timedelta(20, 72000), timedelta(25), timedelta(25, 43200), timedelta(37), timedelta(41, 57600), timedelta(42, 61200)]

