"""
This connects to an IRC network/channel and launches an 'bot' onto it.
The bot then pipes what is being said between the IRC channel and one or
more Evennia channels.
"""

import re
from twisted.application import internet
from twisted.words.protocols import irc
from twisted.internet import protocol
from evennia.server.session import Session
from evennia.utils import logger, utils


# IRC colors

IRC_BOLD = "\002"
IRC_COLOR = "\003"
IRC_RESET = "\017"
IRC_ITALIC = "\026"
IRC_NORMAL = "99"
IRC_UNDERLINE = "37"

IRC_WHITE = "0"
IRC_BLACK = "1"
IRC_DBLUE = "2"
IRC_DGREEN = "3"
IRC_RED = "4"
IRC_DRED = "5"
IRC_DMAGENTA = "6"
IRC_DYELLOW = "7"
IRC_YELLOW = "8"
IRC_GREEN = "9"
IRC_DCYAN = "10"
IRC_CYAN = "11"
IRC_BLUE = "12"
IRC_MAGENTA = "13"
IRC_DGREY = "14"
IRC_GRAY = "15"

# test:
# {rred {ggreen {yyellow {bblue {mmagenta {ccyan {wwhite {xdgrey
# {Rdred {Gdgreen {Ydyellow {Bdblue {Mdmagenta {Cdcyan {Wlgrey {Xblack
# {[rredbg {[ggreenbg {[yyellowbg {[bbluebg {[mmagentabg {[ccyanbg {[wlgreybg {[xblackbg

IRC_COLOR_MAP = dict([
    (r'{n', IRC_RESET),                # reset
    (r'{/', ""),          # line break
    (r'{-', " "),             # tab
    (r'{_', " "),           # space
    (r'{*', ""),        # invert
    (r'{^', ""),          # blinking text

    (r'{r', IRC_COLOR + IRC_RED),
    (r'{g', IRC_COLOR + IRC_GREEN),
    (r'{y', IRC_COLOR + IRC_YELLOW),
    (r'{b', IRC_COLOR + IRC_BLUE),
    (r'{m', IRC_COLOR + IRC_MAGENTA),
    (r'{c', IRC_COLOR + IRC_CYAN),
    (r'{w', IRC_COLOR + IRC_WHITE),  # pure white
    (r'{x', IRC_COLOR + IRC_DGREY),  # dark grey

    (r'{R', IRC_COLOR + IRC_DRED),
    (r'{G', IRC_COLOR + IRC_DGREEN),
    (r'{Y', IRC_COLOR + IRC_DYELLOW),
    (r'{B', IRC_COLOR + IRC_DBLUE),
    (r'{M', IRC_COLOR + IRC_DMAGENTA),
    (r'{C', IRC_COLOR + IRC_DCYAN),
    (r'{W', IRC_COLOR + IRC_GRAY),  # light grey
    (r'{X', IRC_COLOR + IRC_BLACK),  # pure black

    (r'{[r', IRC_COLOR + IRC_NORMAL + "," + IRC_DRED),
    (r'{[g', IRC_COLOR + IRC_NORMAL + "," + IRC_DGREEN),
    (r'{[y', IRC_COLOR + IRC_NORMAL + "," + IRC_DYELLOW),
    (r'{[b', IRC_COLOR + IRC_NORMAL + "," + IRC_DBLUE),
    (r'{[m', IRC_COLOR + IRC_NORMAL + "," + IRC_DMAGENTA),
    (r'{[c', IRC_COLOR + IRC_NORMAL + "," + IRC_DCYAN),
    (r'{[w', IRC_COLOR + IRC_NORMAL + "," + IRC_GRAY),    # light grey background
    (r'{[x', IRC_COLOR + IRC_NORMAL + "," + IRC_BLACK)     # pure black background
    ])
RE_IRC_COLOR = re.compile(r"|".join([re.escape(key) for key in IRC_COLOR_MAP.keys()]), re.DOTALL)
RE_MXP = re.compile(r'\{lc(.*?)\{lt(.*?)\{le', re.DOTALL)
RE_ANSI_ESCAPES = re.compile(r"(%s)" % "|".join(("{{", "%%", "\\\\")), re.DOTALL)

def sub_irc(ircmatch):
    return IRC_COLOR_MAP.get(ircmatch.group(), "")

def parse_irc_colors(string):
    """
    Parse {-type syntax and replace with IRC color markers
    """
    in_string = utils.to_str(string)
    parsed_string = ""
    parts = RE_ANSI_ESCAPES.split(in_string) + [" "]
    for part, sep in zip(parts[::2], parts[1::2]):
        pstring = RE_IRC_COLOR.sub(sub_irc, part)
        parsed_string += "%s%s" % (pstring, sep[0].strip())
    # strip mxp
    parsed_string = RE_MXP.sub(r'\2', parsed_string)
    return parsed_string

# IRC bot

class IRCBot(irc.IRCClient, Session):
    """
    An IRC bot that tracks actitivity in a channel as well
    as sends text to it when prompted
    """
    lineRate = 1

    # assigned by factory at creation

    nickname = None
    logger = None
    factory = None
    channel = None

    def signedOn(self):
        """
        This is called when we successfully connect to
        the network. We make sure to now register with
        the game as a full session.
        """
        self.join(self.channel)
        self.stopping = False
        self.factory.bot = self
        address = "%s@%s" % (self.channel, self.network)
        self.init_session("ircbot", address, self.factory.sessionhandler)
        # we link back to our bot and log in
        self.uid = int(self.factory.uid)
        self.logged_in = True
        self.factory.sessionhandler.connect(self)
        logger.log_infomsg("IRC bot '%s' connected to %s at %s:%s." % (self.nickname, self.channel,
                                                                              self.network, self.port))

    def disconnect(self, reason=None):
        """
        Called by sessionhandler to disconnect this protocol
        """
        print "irc disconnect called!"
        self.sessionhandler.disconnect(self)
        self.stopping = True
        self.transport.loseConnection()

    def privmsg(self, user, channel, msg):
        "A message was sent to channel"
        if not msg.startswith('***'):
            user = user.split('!', 1)[0]
            self.data_in("bot_data_in %s@%s: %s" % (user, channel, msg))

    def action(self, user, channel, msg):
        "An action was done in channel"
        if not msg.startswith('**'):
            user = user.split('!', 1)[0]
            self.data_in("bot_data_in %s@%s %s" % (user, channel, msg))

    def data_in(self, text=None, **kwargs):
        "Data IRC -> Server"
        self.sessionhandler.data_in(self, text=text, **kwargs)

    def data_out(self, text=None, **kwargs):
        "Data from server-> IRC"
        if text.startswith("bot_data_out"):
            text = text.split(" ", 1)[1]
            text = parse_irc_colors(text)
            self.say(self.channel, text)


class IRCBotFactory(protocol.ReconnectingClientFactory):
    """
    Creates instances of AnnounceBot, connecting with
    a staggered increase in delay
    """
    # scaling reconnect time
    initialDelay = 1
    factor = 1.5
    maxDelay = 60

    def __init__(self, sessionhandler, uid=None, botname=None, channel=None, network=None, port=None):
        "Storing some important protocol properties"
        self.sessionhandler = sessionhandler
        self.uid = uid
        self.nickname = str(botname)
        self.channel = str(channel)
        self.network = str(network)
        self.port = port
        self.bot = None

    def buildProtocol(self, addr):
        "Build the protocol and assign it some properties"
        protocol = IRCBot()
        protocol.factory = self
        protocol.nickname = self.nickname
        protocol.channel = self.channel
        protocol.network = self.network
        protocol.port = self.port
        return protocol

    def startedConnecting(self, connector):
        "Tracks reconnections for debugging"
        logger.log_infomsg("(re)connecting to %s" % self.channel)

    def clientConnectionFailed(self, connector, reason):
        self.retry(connector)

    def clientConnectionLost(self, connector, reason):
        if not self.bot.stopping:
            self.retry(connector)

    def start(self):
        "Connect session to sessionhandler"
        if self.port:
            service = internet.TCPClient(self.network, int(self.port), self)
            self.sessionhandler.portal.services.addService(service)
