# Nemubot is a smart and modulable IM bot.
# Copyright (C) 2012-2015  Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Progressive display of very long messages"""

# PYTHON STUFFS #######################################################

import logging

from nemubot.message import Text, DirectAsk
from nemubot.hooks import hook

logger = logging.getLogger("nemubot.response")


# MODULE CORE #########################################################

class Response:

    def __init__(self, message=None, channel=None, nick=None, server=None,
                 nomore="No more message", title=None, more="(suite) ",
                 count=None, shown_first_count=-1, line_treat=None):
        self.nomore = nomore
        self.more = more
        self.line_treat = line_treat
        self.rawtitle = title
        self.server = server
        self.messages = list()
        self.alone = True
        if message is not None:
            self.append_message(message, shown_first_count=shown_first_count)
        self.elt = 0  # Next element to display

        self.channel = channel
        self.nick = nick
        self.count = count


    @property
    def to(self):
        if self.channel is None:
            if self.nick is not None:
                return [self.nick]
            return list()
        elif isinstance(self.channel, list):
            return self.channel
        else:
            return [self.channel]


    def append_message(self, message, title=None, shown_first_count=-1):
        if type(message) is str:
            message = message.split('\n')
            if len(message) > 1:
                for m in message:
                    self.append_message(m)
                return
            else:
                message = message[0]
        if message is not None and len(message) > 0:
            if shown_first_count >= 0:
                self.messages.append(message[:shown_first_count])
                message = message[shown_first_count:]
            self.messages.append(message)
            self.alone = self.alone and len(self.messages) <= 1
            if isinstance(self.rawtitle, list):
                self.rawtitle.append(title)
            elif title is not None:
                rawtitle = self.rawtitle
                self.rawtitle = list()
                for osef in self.messages:
                    self.rawtitle.append(rawtitle)
                self.rawtitle.pop()
                self.rawtitle.append(title)
        return self


    def append_content(self, message):
        if message is not None and len(message) > 0:
            if self.messages is None or len(self.messages) == 0:
                self.messages = [message]
                self.alone = True
            else:
                self.messages[len(self.messages)-1] += message
                self.alone = self.alone and len(self.messages) <= 1
        return self


    @property
    def empty(self):
        return len(self.messages) <= 0


    @property
    def title(self):
        if isinstance(self.rawtitle, list):
            return self.rawtitle[0]
        else:
            return self.rawtitle


    @property
    def text(self):
        if len(self.messages) < 1:
            return self.nomore
        else:
            for msg in self.messages:
                if isinstance(msg, list):
                    return ", ".join(msg)
                else:
                    return msg


    def pop(self):
        self.messages.pop(0)
        self.elt = 0
        if isinstance(self.rawtitle, list):
            self.rawtitle.pop(0)
            if len(self.rawtitle) <= 0:
                self.rawtitle = None


    def accept(self, visitor):
        visitor.visit(self.next_response())


    def next_response(self, maxlen=440):
        if self.nick:
            return DirectAsk(self.nick,
                             self.get_message(maxlen - len(self.nick) - 2),
                             server=None, to=self.to)
        else:
            return Text(self.get_message(maxlen),
                        server=None, to=self.to)


    def __str__(self):
        ret = []
        if len(self.messages):
            for msg in self.messages:
                if isinstance(msg, list):
                    ret.append(", ".join(msg))
                else:
                    ret.append(msg)
        ret.append(self.nomore)
        return "\n".join(ret)

    def get_message(self, maxlen):
        if self.alone and len(self.messages) > 1:
            self.alone = False

        if self.empty:
            if hasattr(self.nomore, '__call__'):
                res = self.nomore(self)
                if res is None:
                    return "No more message"
                elif isinstance(res, Response):
                    self.__dict__ = res.__dict__
                elif isinstance(res, list):
                    self.messages = res
                elif isinstance(res, str):
                    self.messages.append(res)
                else:
                    raise Exception("Type returned by nomore (%s) is not "
                                    "handled here." % type(res))
                return self.get_message()
            else:
                return self.nomore

        if self.line_treat is not None and self.elt == 0:
            if isinstance(self.messages[0], list):
                for x in self.messages[0]:
                    print(x, self.line_treat(x))
                self.messages[0] = [self.line_treat(x).replace("\n", " ").strip() for x in self.messages[0]]
            else:
                self.messages[0] = (self.line_treat(self.messages[0])
                                    .replace("\n", " ").strip())

        msg = ""
        if self.title is not None:
            if self.elt > 0:
                msg += self.title + " " + self.more + ": "
            else:
                msg += self.title + ": "

        elif self.elt > 0:
            msg += "[…]"
            if self.messages[0][self.elt - 1] == ' ':
                msg += " "

        elts = self.messages[0][self.elt:]
        if isinstance(elts, list):
            for e in elts:
                if len(msg) + len(e) > maxlen - 3:
                    msg += "[…]"
                    self.alone = False
                    return msg
                else:
                    msg += e + ", "
                    self.elt += 1
            self.pop()
            return msg[:len(msg)-2]

        else:
            if len(elts.encode()) <= maxlen:
                self.pop()
                if self.count is not None and not self.alone:
                    return msg + elts + (self.count % len(self.messages))
                else:
                    return msg + elts

            else:
                words = elts.split(' ')

                if len(words[0].encode()) > maxlen - len(msg.encode()):
                    self.elt += maxlen - len(msg.encode())
                    return msg + elts[:self.elt] + "[…]"

                for w in words:
                    if len(msg.encode()) + len(w.encode()) >= maxlen:
                        msg += "[…]"
                        self.alone = False
                        return msg
                    else:
                        msg += w + " "
                        self.elt += len(w) + 1
                self.pop()
                return msg


SERVERS = dict()


# MODULE INTERFACE ####################################################

@hook.post()
def parseresponse(res):
    # TODO: handle inter-bot communication NOMORE
    # TODO: check that the response is not the one already saved
    if isinstance(res, Response):
        if res.server not in SERVERS:
            SERVERS[res.server] = dict()
        for receiver in res.to:
            if receiver in SERVERS[res.server]:
                nw, bk = SERVERS[res.server][receiver]
            else:
                nw, bk = None, None
            if nw != res:
                SERVERS[res.server][receiver] = (res, bk)
    return res


@hook.command("more")
def cmd_more(msg):
    """Display next chunck of the message"""
    res = list()
    if msg.server in SERVERS:
        for receiver in msg.to_response:
            if receiver in SERVERS[msg.server]:
                nw, bk = SERVERS[msg.server][receiver]
                if nw is not None and not nw.alone:
                    bk = nw
                    SERVERS[msg.server][receiver] = None, bk
                if bk is not None:
                    res.append(bk)
    return res


@hook.command("next")
def cmd_next(msg):
    """Display the next information include in the message"""
    res = list()
    if msg.server in SERVERS:
        for receiver in msg.to_response:
            if receiver in SERVERS[msg.server]:
                nw, bk = SERVERS[msg.server][receiver]
                if nw is not None and not nw.alone:
                    bk = nw
                    SERVERS[msg.server][receiver] = None, bk
                bk.pop()
                if bk is not None:
                    res.append(bk)
    return res
