# -*- coding: utf-8 -*-
# libavg - Media Playback Engine.
# Copyright (C) 2003-2014 Ulrich von Zadow
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Current versions can be found at www.libavg.de
#
# Original author of this file is Henrik Thoms

from libavg import avg, statemachine, player, gesture

from base import SwitchNode, HVStretchNode
from . import skin

class _ButtonBase(avg.DivNode):

    PRESSED = avg.Publisher.genMessageID()
    RELEASED = avg.Publisher.genMessageID()

    def __init__(self, parent=None, **kwargs):
        super(_ButtonBase, self).__init__(**kwargs)
        self.registerInstance(self, parent)
        self.publish(self.PRESSED)
        self.publish(self.RELEASED)

    def _setActiveArea(self, upNode, activeAreaNode, fatFingerEnlarge):
        if fatFingerEnlarge:
            if activeAreaNode:
                raise(RuntimeError(
                    "Button: Can't specify both fatFingerEnlarge and activeAreaNode"))
            size = upNode.size
            minSize = 20*player.getPixelsPerMM()
            size = avg.Point2D(max(minSize, size.x), max(minSize, size.y))
            activeAreaNode = avg.RectNode(size=size, opacity=0, parent=self)
            #Here we need to store a 'hard' reference to the active area node
            #because the tap recognizer won't keep it
            self.__activeAreaNode = activeAreaNode
        else:
            if activeAreaNode == None:
                activeAreaNode = self
            else:
                self.appendChild(activeAreaNode)

        self._tapRecognizer = gesture.TapRecognizer(activeAreaNode,
                possibleHandler=self._onDown, 
                detectedHandler=self._onTap, 
                failHandler=self._onTapFail)


class Button(_ButtonBase):

    CLICKED = avg.Publisher.genMessageID()

    def __init__(self, upNode, downNode, disabledNode=None, activeAreaNode=None, 
            enabled=True, fatFingerEnlarge=False, **kwargs):
        super(Button, self).__init__(**kwargs)

        if disabledNode == None:
            disabledNode = upNode

        nodeMap = {
            "UP": upNode, 
            "DOWN": downNode, 
            "DISABLED": disabledNode
        }
        self.__switchNode = SwitchNode(nodeMap=nodeMap, visibleid="UP", parent=self)
        self.publish(self.CLICKED)

        self.__stateMachine = statemachine.StateMachine("Button", "UP")
        self.__stateMachine.addState("UP", ("DOWN", "DISABLED"),
                enterFunc=self._enterUp, leaveFunc=self._leaveUp)
        self.__stateMachine.addState("DOWN", ("UP", "DISABLED"),
                enterFunc=self._enterDown, leaveFunc=self._leaveDown)
        self.__stateMachine.addState("DISABLED", ("UP",),
                enterFunc=self._enterDisabled, leaveFunc=self._leaveDisabled)

        self._setActiveArea(upNode, activeAreaNode, fatFingerEnlarge)

        if not(enabled):
            self.setEnabled(False)

    def getEnabled(self):
        return self.__stateMachine.state != "DISABLED"

    def setEnabled(self, enabled):
        if enabled:
            if self.__stateMachine.state == "DISABLED":
                self.__stateMachine.changeState("UP")
        else:
            if self.__stateMachine.state != "DISABLED":
                self.__stateMachine.changeState("DISABLED")

    enabled = property(getEnabled, setEnabled)

    def _getState(self):
        return self.__stateMachine.state

    def _onDown(self):
        self.__stateMachine.changeState("DOWN")
        self.notifySubscribers(self.PRESSED, [])

    def _onTap(self):
        self.__stateMachine.changeState("UP")
        self.notifySubscribers(self.CLICKED, [])
        self.notifySubscribers(self.RELEASED, [])

    def _onTapFail(self):
        self.__stateMachine.changeState("UP")
        self.notifySubscribers(self.RELEASED, [])

    def _enterUp(self):
        self.__setActiveNode()

    def _leaveUp(self):
        pass

    def _enterDown(self):
        self.__setActiveNode()

    def _leaveDown(self):
        pass

    def _enterDisabled(self):
        self.__setActiveNode()
        self._tapRecognizer.enable(False)

    def _leaveDisabled(self):
        self._tapRecognizer.enable(True)

    def __setActiveNode(self):
        self.__switchNode.visibleid = self.__stateMachine.state


class BmpButton(Button):

    def __init__(self, upSrc, downSrc, disabledSrc=None, **kwargs):
        upNode = avg.ImageNode(href=upSrc)
        downNode = avg.ImageNode(href=downSrc)
        if disabledSrc != None:
            disabledNode = avg.ImageNode(href=disabledSrc)
        else:
            disabledNode = None
        super(BmpButton, self).__init__(upNode=upNode, downNode=downNode, 
                disabledNode=disabledNode, **kwargs)


class TextButton(Button):

    def __init__(self, text, skinObj=skin.Skin.default, **kwargs):
        size = avg.Point2D(kwargs["size"])
        cfg = skinObj.defaultTextButtonCfg

        self.wordsNodes = []

        upNode = self.__createStateNode(size, cfg, "upBmp", text, "font")
        downNode = self.__createStateNode(size, cfg, "downBmp", text, "downFont")
        if "disabledBmp" in cfg:
            disabledNode = self.__createStateNode(size, cfg, "disabledBmp", text, 
                    "disabledFont")
        else:
            disabledNode = None
        
        super(TextButton, self).__init__(upNode=upNode, downNode=downNode,
                disabledNode=disabledNode, **kwargs)

    def __createStateNode(self, size, cfg, bmpName, text, fontStyleName):
        stateNode = avg.DivNode(size=size)
        endsExtent = eval(cfg["endsExtent"], {}, {})
        HVStretchNode(size=size, src=cfg[bmpName], endsExtent=endsExtent, 
                parent=stateNode)
        words = avg.WordsNode(text=text, fontstyle=cfg[fontStyleName], parent=stateNode)
        words.pos = (round((size.x-words.size.x)/2), round((size.y-words.size.y)/2))
        self.wordsNodes.append(words)
        return stateNode

    def getText(self):
        return self.wordsNodes[0].text

    def setText(self, text):
        for node in self.wordsNodes:
            node.text = text
            node.pos = (self.size-node.size)/2
    
    text = property(getText, setText)
    

class ToggleButton(_ButtonBase):
    
    TOGGLED = avg.Publisher.genMessageID()

    def __init__(self, uncheckedUpNode, uncheckedDownNode, checkedUpNode, checkedDownNode,
            uncheckedDisabledNode=None, checkedDisabledNode=None, activeAreaNode=None,
            enabled=True, fatFingerEnlarge=False, checked=False, **kwargs):
        super(ToggleButton, self).__init__(**kwargs)
        nodeMap = {
            "UNCHECKED_UP": uncheckedUpNode, 
            "UNCHECKED_DOWN": uncheckedDownNode, 
            "CHECKED_UP": checkedUpNode, 
            "CHECKED_DOWN": checkedDownNode, 
            "UNCHECKED_DISABLED": uncheckedDisabledNode, 
            "CHECKED_DISABLED": checkedDisabledNode, 
        }
        if uncheckedDisabledNode == None:
            nodeMap["UNCHECKED_DISABLED"] = uncheckedUpNode
        if checkedDisabledNode == None:
            nodeMap["CHECKED_DISABLED"] = checkedUpNode
        self.__switchNode = SwitchNode(nodeMap=nodeMap, visibleid="UNCHECKED_UP", 
                parent=self)

        self.publish(ToggleButton.TOGGLED)

        self.__stateMachine = statemachine.StateMachine("ToggleButton", "UNCHECKED_UP")
        self.__stateMachine.addState("UNCHECKED_UP", ("UNCHECKED_DOWN",
                "UNCHECKED_DISABLED"), enterFunc=self._enterUncheckedUp,
                leaveFunc=self._leaveUncheckedUp)
        self.__stateMachine.addState("UNCHECKED_DOWN", ("UNCHECKED_UP",
                "UNCHECKED_DISABLED", "CHECKED_UP"), enterFunc=self._enterUncheckedDown,
                leaveFunc=self._leaveUncheckedDown)
        self.__stateMachine.addState("CHECKED_UP", ("CHECKED_DOWN", "CHECKED_DISABLED"),
                enterFunc=self._enterCheckedUp, leaveFunc=self._leaveCheckedUp)
        self.__stateMachine.addState("CHECKED_DOWN", ("CHECKED_UP", "UNCHECKED_UP",
                "CHECKED_DISABLED"), enterFunc=self._enterCheckedDown,
                leaveFunc=self._leaveCheckedDown)
        self.__stateMachine.addState("UNCHECKED_DISABLED", ("UNCHECKED_UP",),
                enterFunc=self._enterUncheckedDisabled,
                leaveFunc=self._leaveUncheckedDisabled)
        self.__stateMachine.addState("CHECKED_DISABLED", ("CHECKED_UP", ),
                enterFunc=self._enterCheckedDisabled,
                leaveFunc=self._leaveCheckedDisabled)

        self._setActiveArea(uncheckedUpNode, activeAreaNode, fatFingerEnlarge)

        if not enabled:
            self.__stateMachine.changeState("UNCHECKED_DISABLED")
        if checked:
            self.setChecked(True)

    def getEnabled(self):
        return (self.__stateMachine.state != "CHECKED_DISABLED" and
                self.__stateMachine.state != "UNCHECKED_DISABLED")

    def setEnabled(self, enabled):
        if enabled:
            if self.__stateMachine.state == "CHECKED_DISABLED":
                self.__stateMachine.changeState("CHECKED_UP")
            elif self.__stateMachine.state == "UNCHECKED_DISABLED":
                self.__stateMachine.changeState("UNCHECKED_UP")
        else:
            if (self.__stateMachine.state == "CHECKED_UP" or
                    self.__stateMachine.state == "CHECKED_DOWN") :
                self.__stateMachine.changeState("CHECKED_DISABLED")
            elif (self.__stateMachine.state == "UNCHECKED_UP" or
                    self.__stateMachine.state == "UNCHECKED_DOWN") :
                self.__stateMachine.changeState("UNCHECKED_DISABLED")

    enabled = property(getEnabled, setEnabled)

    def getChecked(self):
        return (self.__stateMachine.state != "UNCHECKED_UP" and
                self.__stateMachine.state != "UNCHECKED_DOWN" and
                self.__stateMachine.state != "UNCHECKED_DISABLED")

    def setChecked(self, checked):
        oldEnabled = self.getEnabled()
        if checked:
            if self.__stateMachine.state == "UNCHECKED_DISABLED":
                self.__stateMachine.changeState("UNCHECKED_UP")
            if self.__stateMachine.state == "UNCHECKED_UP":
                self.__stateMachine.changeState("UNCHECKED_DOWN")
            if self.__stateMachine.state != "CHECKED_UP":
                self.__stateMachine.changeState("CHECKED_UP")
            if not oldEnabled:
                self.__stateMachine.changeState("CHECKED_DISABLED")
        else:
            if self.__stateMachine.state == "CHECKED_DISABLED":
                self.__stateMachine.changeState("CHECKED_UP")
            if self.__stateMachine.state == "CHECKED_UP":
                self.__stateMachine.changeState("CHECKED_DOWN")
            if self.__stateMachine.state != "UNCHECKED_UP":
                self.__stateMachine.changeState("UNCHECKED_UP")
            if not oldEnabled:
                self.__stateMachine.changeState("UNCHECKED_DISABLED")

    checked = property(getChecked, setChecked)

    def _getState(self):
        return self.__stateMachine.state

    def _enterUncheckedUp(self):
        self.__setActiveNode()

    def _leaveUncheckedUp(self):
        pass

    def _enterUncheckedDown(self):
        self.__setActiveNode()

    def _leaveUncheckedDown(self):
        pass

    def _enterCheckedUp(self):
        self.__setActiveNode()

    def _leaveCheckedUp(self):
        pass

    def _enterCheckedDown(self):
        self.__setActiveNode()

    def _leaveCheckedDown(self):
        pass

    def _enterUncheckedDisabled(self):
        self.__setActiveNode()
        self._tapRecognizer.enable(False)

    def _leaveUncheckedDisabled(self):
        self._tapRecognizer.enable(True)

    def _enterCheckedDisabled(self):
        self.__setActiveNode()
        self._tapRecognizer.enable(False)

    def _leaveCheckedDisabled(self):
        self._tapRecognizer.enable(True)

    def _onDown(self):
        if self.__stateMachine.state == "UNCHECKED_UP":
            self.__stateMachine.changeState("UNCHECKED_DOWN")
        elif self.__stateMachine.state == "CHECKED_UP":
            self.__stateMachine.changeState("CHECKED_DOWN")
        self.notifySubscribers(self.PRESSED, [])

    def _onTap(self):
        if self.__stateMachine.state == "UNCHECKED_DOWN":
            self.__stateMachine.changeState("CHECKED_UP")
            self.notifySubscribers(ToggleButton.TOGGLED, [True])
        elif self.__stateMachine.state == "CHECKED_DOWN":
            self.__stateMachine.changeState("UNCHECKED_UP")
            self.notifySubscribers(ToggleButton.TOGGLED, [False])
        self.notifySubscribers(self.RELEASED, [])

    def _onTapFail(self):
        if self.__stateMachine.state == "UNCHECKED_DOWN":
            self.__stateMachine.changeState("UNCHECKED_UP")
        elif self.__stateMachine.state == "CHECKED_DOWN":
            self.__stateMachine.changeState("CHECKED_UP")
        self.notifySubscribers(self.RELEASED, [])
    
    def __setActiveNode(self):
        self.__switchNode.visibleid = self.__stateMachine.state


class CheckBox(ToggleButton):

    def __init__(self, text="", skinObj=skin.Skin.default, **kwargs):
        self.cfg = skinObj.defaultCheckBoxCfg
        
        uncheckedUpNode = self.__createImageNode(self.cfg["uncheckedUpBmp"])
        uncheckedDownNode = self.__createImageNode(self.cfg["uncheckedDownBmp"])
        uncheckedDisabledNode = self.__createImageNode(self.cfg["uncheckedDisabledBmp"])
        checkedUpNode = self.__createImageNode(self.cfg["checkedUpBmp"])
        checkedDownNode = self.__createImageNode(self.cfg["checkedDownBmp"])
        checkedDisabledNode = self.__createImageNode(self.cfg["checkedDisabledBmp"])

        super(CheckBox, self).__init__(uncheckedUpNode=uncheckedUpNode,
                uncheckedDownNode=uncheckedDownNode, 
                uncheckedDisabledNode=uncheckedDisabledNode,
                checkedUpNode=checkedUpNode,
                checkedDownNode=checkedDownNode, 
                checkedDisabledNode=checkedDisabledNode,
                **kwargs)
        self.textNode = avg.WordsNode(pos=(20,0), text=text, fontstyle=self.cfg["font"],
                parent=self)

    def _enterUncheckedUp(self):
        self.textNode.fontstyle = self.cfg["font"]
        super(CheckBox, self)._enterUncheckedUp()

    def _enterUncheckedDown(self):
        self.textNode.fontstyle = self.cfg["downFont"]
        super(CheckBox, self)._enterUncheckedDown()

    def _enterCheckedUp(self):
        self.textNode.fontstyle = self.cfg["font"]
        super(CheckBox, self)._enterCheckedUp()

    def _enterCheckedDown(self):
        self.textNode.fontstyle = self.cfg["downFont"]
        super(CheckBox, self)._enterCheckedDown()

    def _enterUncheckedDisabled(self):
        self.textNode.fontstyle = self.cfg["disabledFont"]
        super(CheckBox, self)._enterUncheckedDisabled()

    def _enterCheckedDisabled(self):
        self.textNode.fontstyle = self.cfg["disabledFont"]
        super(CheckBox, self)._enterCheckedDisabled()

    def __createImageNode(self, bmp):
        node = avg.ImageNode()
        node.setBitmap(bmp)
        return node


class BmpToggleButton(ToggleButton):
    def __init__(self, uncheckedUpSrc, uncheckedDownSrc, checkedUpSrc, checkedDownSrc,
            uncheckedDisabledSrc=None, checkedDisabledSrc=None, **kwargs):
        uncheckedUpNode = avg.ImageNode(href=uncheckedUpSrc)
        uncheckedDownNode = avg.ImageNode(href=uncheckedDownSrc)
        checkedUpNode = avg.ImageNode(href=checkedUpSrc)
        checkedDownNode = avg.ImageNode(href=checkedDownSrc)

        if uncheckedDisabledSrc != None:
            uncheckedDisabledNode = avg.ImageNode(href=uncheckedDisabledSrc)
        else:
            uncheckedDisabledNode = None
        if checkedDisabledSrc != None:
            checkedDisabledNode = avg.ImageNode(href=checkedDisabledSrc)
        else:
            checkedDisabledNode = None

        super(BmpToggleButton, self).__init__(uncheckedUpNode=uncheckedUpNode,
                uncheckedDownNode=uncheckedDownNode, 
                checkedUpNode=checkedUpNode,
                checkedDownNode=checkedDownNode,
                uncheckedDisabledNode=uncheckedDisabledNode,
                checkedDisabledNode=checkedDisabledNode, 
                **kwargs)

