#
# Copyright (c) 2004 Art Haas
#
# This file is part of PythonCAD.
#
# PythonCAD is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PythonCAD 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PythonCAD; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# A base class for objects that send messages
#

import types

class Messenger(object):
    messages = {
        'connected' : True,
        'disconnected' : True
        }
    def __init__(self):
        self.__connections = None
        self.__ignore = None
        self.__muted = False

    def finish(self):
        if self.__connections is not None:
            print "remaining connections for obj: " + `self`
            for _message in self.__connections:
                print "message: %s" % _message
                for _t in self.__connections[_message]:
                    _obj, _meth = _t
                    print "connected to obj: " + `_obj`

    def connect(self, message, obj, method):
        if not self.sendsMessage(message):
            raise ValueError, "Unknown message : %s" % str(message)
        if not isinstance(method, types.MethodType):
            raise TypeError, "Invalid class method: %s" % str(method)
        if not isinstance(obj, method.im_class):
            raise ValueError, "Unknown method for object: %s" % str(method)
        if self.__connections is None:
            self.__connections = {}
        _tups = self.__connections.setdefault(message, [])
        _seen = False
        for _tup in _tups[:]:
            _obj = _tup[0]
            if _obj is obj:
                _seen = True
                break
        if not _seen:
            _tups.append((obj, method))
            self.sendMessage('connected', obj, method)

    def disconnect(self, obj, message=None):
        if self.__connections is not None:
            if message is None:
                _connections = self.__connections.keys()
                for _m in _connections:
                    _tups = self.__connections[_m]
                    for _tup in _tups[:]:
                        _obj = _tup[0]
                        if _obj is obj:
                            _tups.remove(_tup)
                            self.sendMessage('disconnected', obj)
                    if len(_tups) == 0:
                        del self.__connections[_m]
            else:
                if message in self.__connections:
                    _tups = self.__connections[message]
                    for _tup in _tups[:]:
                        _obj = _tup[0]
                        if _obj is obj:
                            _tups.remove(_tup)
                            self.sendMessage('disconnected', obj)
                            break
                    if len(_tups) == 0:
                        del self.__connections[message]
            if len(self.__connections) == 0:
                self.__connections = None

    def sendsMessage(self, m):
        return m in Messenger.messages

    def sendMessage(self, message, *args):
        if not isinstance(message, str):
            raise TypeError, "Message name not a string: " + `message`
        if not self.__muted and self.__connections is not None:
            if message in self.__connections:
                _tups = self.__connections[message][:] # make a copy
                for _obj, _method in _tups:
                    if isinstance(_obj, Messenger) and _obj.ignores(message):
                        continue
                    _method(_obj, self, *args)
    def mute(self):
        self.__muted = True

    def unmute(self):
        self.__muted = False

    def isMuted(self):
        return self.__muted

    def ignore(self, message):
        if not isinstance(message, str):
            raise TypeError, "Invalid message name: " + `message`
        if self.__ignore is None:
            self.__ignore = {}
        if message in self.__ignore:
            raise RuntimeError, "Message '%s' already ignored." % message
        self.__ignore[message] = True

    def receive(self, message):
        if not isinstance(message, str):
            raise TypeError, "Invalid message name: " + `message`
        if self.__ignore is not None:
            if message in self.__ignore:
                del self.__ignore[message]
            if len(self.__ignore) == 0:
                self.__ignore = None

    def ignores(self, message):
        return self.__ignore is not None and message in self.__ignore
        
    def receives(self, message):
        return self.__ignore is None or message not in self.__ignore
