from panda3d.core import TextNode, PGButton, Point3

from toontown.chat import ChatGlobals
from toontown.chat.ChatBalloon import ChatBalloon
from toontown.margins import MarginGlobals
from toontown.margins.MarginVisible import MarginVisible
from toontown.nametag import NametagGlobals
from toontown.toontowngui.Clickable2d import Clickable2d


class WhisperQuitButton(Clickable2d):
    CONTENTS_SCALE = 12

    def __init__(self, whisperPopup):
        Clickable2d.__init__(self, 'WhisperQuitButton')

        self.whisperPopup = whisperPopup

        self.contents.setScale(self.CONTENTS_SCALE)
        self.contents.hide()

        self.nodePath = None

        self.update()

    def destroy(self):
        self.ignoreAll()

        if self.nodePath is not None:
            self.nodePath.removeNode()
            self.nodePath = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'WhisperQuitButton-' + str(id(self))

    def update(self):
        if self.nodePath is not None:
            self.nodePath.removeNode()
            self.nodePath = None

        self.contents.node().removeAllChildren()

        quitButtonNode = NametagGlobals.quitButton[self.clickState]
        self.nodePath = quitButtonNode.copyTo(self.contents)

    def applyClickState(self, clickState):
        if self.nodePath is not None:
            self.nodePath.removeNode()
            self.nodePath = None

        quitButtonNode = NametagGlobals.quitButton[clickState]
        self.nodePath = quitButtonNode.copyTo(self.contents)

    def setClickState(self, clickState):
        self.applyClickState(clickState)

        if self.isHovering() or self.whisperPopup.isHovering():
            self.contents.show()
        elif self.clickState == PGButton.SDepressed:
            self.contents.show()
        else:
            self.contents.hide()

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.lastClickState != PGButton.SDepressed:
            base.playSfx(NametagGlobals.rolloverSound)

    def updateClickRegion(self):
        if self.nodePath is not None:
            right = NametagGlobals.quitButtonWidth / 2.0
            left = -right
            top = NametagGlobals.quitButtonHeight / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)


class WhisperPopup(Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25

    TEXT_MAX_ROWS = 6
    TEXT_WORD_WRAP = 8

    QUIT_BUTTON_SHIFT = (0.42, 0, 0.42)

    WHISPER_TIMEOUT_MIN = 10
    WHISPER_TIMEOUT_MAX = 20

    def __init__(self, text, font, whisperType, timeout=None):
        Clickable2d.__init__(self, 'WhisperPopup')
        MarginVisible.__init__(self)

        self.text = text
        self.font = font
        self.whisperType = whisperType
        if timeout is None:
            self.timeout = len(text) * 0.33
            if self.timeout < self.WHISPER_TIMEOUT_MIN:
                self.timeout = self.WHISPER_TIMEOUT_MIN
            elif self.timeout > self.WHISPER_TIMEOUT_MAX:
                self.timeout = self.WHISPER_TIMEOUT_MAX
        else:
            self.timeout = timeout

        self.active = False

        self.senderName = ''
        self.fromId = 0

        self.contents.setScale(self.CONTENTS_SCALE)

        self.whisperColor = ChatGlobals.WhisperColors[self.whisperType]

        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setTextColor(self.whisperColor[PGButton.SInactive][0])
        self.textNode.setFont(self.font)
        self.textNode.setText(self.text)

        self.chatBalloon = None
        self.quitButton = None

        self.timeoutTaskName = self.getUniqueName() + '-timeout'
        self.timeoutTask = None

        self.quitEvent = self.getUniqueName() + '-quit'
        self.accept(self.quitEvent, self.destroy)

        self.setPriority(MarginGlobals.MP_high)
        self.setVisible(True)

        self.update()

        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()

        if self.timeoutTask is not None:
            taskMgr.remove(self.timeoutTask)
            self.timeoutTask = None

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.textNode = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'WhisperPopup-' + str(id(self))

    def update(self):
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        self.draw()

        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(
            NametagGlobals.chatBalloon2dModel,
            NametagGlobals.chatBalloon2dWidth,
            NametagGlobals.chatBalloon2dHeight, self.textNode,
            foreground=foreground, background=background
        )
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(
            self.chatBalloon.textNodePath,
            ((left+right) / 2.0, 0, (bottom+top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

        # Draw the quit button:
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)

        # Move the quit button to the top right of the TextNode:
        quitButtonNodePath.setPos(self.contents.getRelativePoint(
            self.chatBalloon.textNodePath, (right, 0, top)))

        # Apply the quit button shift:
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)

        # Allow the quit button to close this whisper:
        self.quitButton.setClickEvent(self.quitEvent)

    def manage(self, marginManager):
        MarginVisible.manage(self, marginManager)

        self.timeoutTask = taskMgr.doMethodLater(
            self.timeout, self.unmanage, self.timeoutTaskName, [marginManager])

    def unmanage(self, marginManager):
        MarginVisible.unmanage(self, marginManager)

        self.destroy()

    def setClickable(self, senderName, fromId):
        self.senderName = senderName
        self.fromId = fromId
        self.setClickEvent('clickedWhisper', extraArgs=[fromId])
        self.setActive(True)

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.whisperColor[clickState]
            self.chatBalloon.setForeground(foreground)
            self.chatBalloon.setBackground(background)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        if self.isHovering() or self.quitButton.isHovering():
            self.quitButton.contents.show()
        elif self.quitButton.getClickState() == PGButton.SDepressed:
            self.quitButton.contents.show()
        else:
            self.quitButton.contents.hide()

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        else:
            if self.region is not None:
                self.region.setActive(False)

        if self.quitButton is not None:
            self.quitButton.updateClickRegion()

    def marginVisibilityChanged(self):
        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return

        origin = Point3()

        self.contents.setPos(origin)

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        if (self.cell in base.leftCells) or (self.cell in base.rightCells):
            text = self.text.replace('\x01WLDisplay\x01', '').replace('\x02', '')
            textWidth = self.textNode.calcWidth(text)
            if (textWidth / self.TEXT_WORD_WRAP) > self.TEXT_MAX_ROWS:
                self.textNode.setWordwrap(textWidth / (self.TEXT_MAX_ROWS-0.5))
        else:
            self.textNode.setWordwrap(self.TEXT_WORD_WRAP)

        self.draw()

        left, right, bottom, top = self.textNode.getFrameActual()
        if self.cell in base.bottomCells:
            # Move the origin to the bottom center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, ((left+right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            # Move the origin to the left center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, (left, 0, (bottom+top) / 2.0))
        elif self.cell in base.rightCells:
            # Move the origin to the right center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, (right, 0, (bottom+top) / 2.0))

        self.contents.setPos(self.contents, -origin)
