# This file was automatically generated by SWIG (https://www.swig.org).
# Version 4.1.1
#
# Do not make changes to this file unless you know what you are doing - modify
# the SWIG interface file instead.

from sys import version_info as _swig_python_version_info
# Import the low-level C/C++ module
if __package__ or "." in __name__:
    from . import _switchboard
else:
    import _switchboard

try:
    import builtins as __builtin__
except ImportError:
    import __builtin__

def _swig_repr(self):
    try:
        strthis = "proxy of " + self.this.__repr__()
    except __builtin__.Exception:
        strthis = ""
    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)


def _swig_setattr_nondynamic_instance_variable(set):
    def set_instance_attr(self, name, value):
        if name == "this":
            set(self, name, value)
        elif name == "thisown":
            self.this.own(value)
        elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
            set(self, name, value)
        else:
            raise AttributeError("You cannot add instance attributes to %s" % self)
    return set_instance_attr


def _swig_setattr_nondynamic_class_variable(set):
    def set_class_attr(cls, name, value):
        if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
            set(cls, name, value)
        else:
            raise AttributeError("You cannot add class attributes to %s" % cls)
    return set_class_attr


def _swig_add_metaclass(metaclass):
    """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
    def wrapper(cls):
        return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return wrapper


class _SwigNonDynamicMeta(type):
    """Meta class to enforce nondynamic attributes (no new attributes) for a class"""
    __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)



def init_switchboard_api(pythonCallable):
    return _switchboard.init_switchboard_api(pythonCallable)
class OOFMessage(object):
    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def __init__(self, arg2):
        _switchboard.OOFMessage_swiginit(self, _switchboard.new_OOFMessage(arg2))

    def name(self):
        return _switchboard.OOFMessage_name(self)

    def __len__(self):
        return _switchboard.OOFMessage___len__(self)

    def __getitem__(self, which):
        return _switchboard.OOFMessage___getitem__(self, which)
    __swig_destroy__ = _switchboard.delete_OOFMessage

# Register OOFMessage in _switchboard:
_switchboard.OOFMessage_swigregister(OOFMessage)
# -*- python -*-

# This software was produced by NIST, an agency of the U.S. government,
# and by statute is not subject to copyright in the United States.
# Recipients of this software assume all responsibilities associated
# with its operation, modification and maintenance. However, to
# facilitate maintenance we ask that before distributing modified
# versions of this software, you first contact the authors at
# oof_manager@nist.gov. 


# Class to handle communication between various pieces of OOF.
# Classes can send the switchboard a message when they've done
# something important, and can request callbacks when the Switchboard
# receives a message.

from ooflib.SWIG.common import lock
from ooflib.SWIG.common import threadstate
from ooflib.common import atshutdown
from ooflib.common import debug
from ooflib.common import subthread
from ooflib.common import thread_enable
from ooflib.common import mainthread

#=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=#

# For debugging, it's useful to know the series of messages that have
# led to the current situation, so each switchboard call stores a
# stack of switchboard messages that came before it on the current
# thread, and passes the stack to a new thread if a callback is called
# on a different thread.

# Because this incurs some overhead for each Switchboard call, it can
# be turned on and off by calling useMessageStack(bool). 

# Calling switchboard.messageStack.current() at any point returns a
# list of switchboard messages whose callbacks haven't returned.
# Nested lists indicate calls on another thread.  Eg, [[A, B], C]
# means that message A was emitted, and its callback directly or
# indirectly emitted message B, and B's callback directly or
# indirectly emitted message C, which ran on a different thread.

class _MessageStack(object):
    def __init__(self):
        self.mdict = {}
        self.lock = lock.SLock()
    def push(self, msg):
        self.lock.acquire()
        try:
            threadno = threadstate.findThreadNumber()
            try:
                stack = self.mdict[threadno]
            except KeyError:
                stack = []
                self.mdict[threadno] = stack
            stack.append(msg)
        finally:
            self.lock.release()
    def pop(self):
        self.lock.acquire()
        try:
            threadno = threadstate.findThreadNumber()
            stack = self.mdict[threadno]
            stack.pop()
        finally:
            self.lock.release()
    def current(self):
        self.lock.acquire()
        try:
            threadno = threadstate.findThreadNumber()
            try:
                stack = self.mdict[threadno]
            except KeyError:
                return []
            return stack[:]
        finally:
            self.lock.release()

# Function wrapper used to pass the message stack when the switchboard
# callback will be called on a different thread than the one that
# issued the notification.  This is used in the subthread and
# mainthreadGUI modules.

class _StackWrap(object):
    def __init__(self, func):
# Called on one thread
        self.func = func
# Save this thread's stack, to prepend to the next
# thread's stack.
        self.stack = messageStack.current()
    def __call__(self, *args, **kwargs):
# Called on a different thread.
        messageStack.push(self.stack)
        try:
            val = self.func(*args, **kwargs)
        finally:
            messageStack.pop()
        return val

# No-op versions of _MessageStack and _StackWrap
class _MessageStackDummy(object):
    def push(self, msg):
        pass
    def pop(self):
        pass
    def current(self):
        return None
def _StackWrapDummy(func):
    return func

# useMessageStackFlag is set by calling useMessageStack.  It's
# initialized to None to ensure that it's callback runs the first time
# it's called.  Its default value should be set by changing it in the
# first call to useMessageStack, below.
useMessageStackFlag = None      

def useMessageStack(yesno):
    global useMessageStackFlag
    global messageStack, StackWrap
    if yesno == useMessageStackFlag:
        return
    if yesno:
        messageStack = _MessageStack()
        StackWrap = _StackWrap
    else:
        messageStack = _MessageStackDummy()
        StackWrap = _StackWrapDummy
    useMessageStackFlag = yesno

def useMessageStackCB(menuitem, flag):
    useMessageStack(flag)

# This sets the default behavior.  It should be False for efficiency,
# and True for debugging.
useMessageStack(False)

# Turn off switchboard stack tracking at shutdown, because it doesn't
# work once threads have been turned off.
atshutdown.atShutDown(useMessageStack, False)

#=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=##=--=#

class _SwitchboardCallbackBase(object):
    def __init__(self, msgname, func, caller, args, kwargs):
        self.msgname = msgname
        self.caller = caller            # code line that requested the callback
        self.func = func
        self.args = args
        self.kwargs = kwargs
        self.blocked = 0
#     def __del__(self):
#         debug.fmsg("Deleting", self.__class__.__name__, self.func)
    def block(self):
        self.blocked += 1
    def unblock(self):
        self.blocked -= 1
    def is_blocked(self):
        return self.blocked > 0
    def perform(self, *args, **kwargs):
        pass
    def __repr__(self):
        return 'SwitchboardCallbackBase(%s, %s)' % (repr(self.msgname), repr(self.func))


# Transfer control to the main thread.  
class _SwitchboardCallback (_SwitchboardCallbackBase):
    def __init__(self, msgname, func, caller, args, kwargs):
        _SwitchboardCallbackBase.__init__(self, msgname, func, caller,
                                          args, kwargs)
    def perform(self, *args, **kwargs):
        if not self.is_blocked():  # If block is zero or negative, do the call.
            kwargs.update(self.kwargs)      # is this safe?
            if switchboard.verbose:
                debug.fmsg("requested by", self.caller)
            messageStack.push(self.msgname)
            try:
                mainthread.runBlock(self.func, self.args + args, kwargs)
            finally:
                messageStack.pop()

    def __repr__(self):
        return 'SwitchboardCallback(%s, %s)' % (repr(self.msgname), repr(self.func))


# Do the callback immediately on the local thread, unless the local
# thread is the main thread.  This is the default.
class _SwitchboardCallbackThread (_SwitchboardCallbackBase):
    def __init__(self, msgname, func, caller, args, kwargs):
        _SwitchboardCallbackBase.__init__(self, msgname, func, caller,
                                          args, kwargs)

    def perform(self, *args, **kwargs):
        if not self.is_blocked():  # If block is zero or negative, do the call.
            kwargs.update(self.kwargs)      # is this safe?
            if switchboard.verbose:
                debug.fmsg("requested by", self.caller)
            messageStack.push(self.msgname)
            try:
                if not mainthread.mainthread():
                    self.func(*(self.args + args), **kwargs)
                else:
                    subthread.execute(self.func, self.args+args, kwargs)
            finally:
                messageStack.pop()

    def __repr__(self):
        return 'SwitchboardCallbackThread(%s, %s)' % (repr(self.msgname),
                                                      repr(self.func))

class _SwitchboardTaskList(object):
    def __init__(self, msgname):
        self.callbacks = []
        self.msgname = msgname
        self.lock = lock.SLock()
#     def __del__(self):
#         debug.fmsg("Deleting sb task list", self.msgname)
    def registerCallback(self, func, caller, args, kwargs, child_thread):
        if not child_thread:
            cb = _SwitchboardCallback(self.msgname, func, caller, args, kwargs)
        else:
            cb = _SwitchboardCallbackThread(self.msgname, func, caller,
                                            args, kwargs)
        self.lock.acquire()
        try:
            self.callbacks.append(cb)
        finally:
            self.lock.release()
        return cb
    def removeCallback(self, callback):
        self.lock.acquire()
        try:
            self.callbacks.remove(callback)
#             debug.fmsg("removed", callback)
        finally:
            self.lock.release()
    def perform(self, *args, **kwargs):
# Callbacks sometimes remove themselves, which modifies the
# self.callbacks list, so iterate over a copy, and acquire
# that copy while the list is locked.
        self.lock.acquire()
        try:
            callbacks = self.callbacks[:]
        finally:
            self.lock.release()
        if switchboard.verbose:
            debug.fmsg("Performing", len(callbacks), "tasks:")
            for callback in callbacks:
                debug.fmsg("    ", callback)
        for callback in callbacks:
            callback.perform(*args, **kwargs)
    def nCallbacks(self):
        self.lock.acquire()
        try:
            return len(self.callbacks)
        finally:
            self.lock.release()


class _Switchboard(object):
    def __init__(self):
        self.taskdict = {}
        self.verbose = False
        self.lock = lock.SLock()
        self.depth = 0
    def _defineMessage(self, msgname):
        tasklist = _SwitchboardTaskList(msgname)
        self.taskdict[msgname] = tasklist
        return tasklist
    def notify(self, msgname, *args, **kwargs):
        self.lock.acquire()
        try:
            task = self.taskdict[msgname]
        except KeyError:
            return
        finally:
            self.lock.release()

        if self.verbose:
            debug.fmsg('performing', msgname, args, kwargs,
                       "depth=%d"%self.depth)
            self.depth += 1
        task.perform(*args, **kwargs)
        if self.verbose:
            self.depth -= 1
            debug.fmsg("done", msgname, "depth=%d"%self.depth)

    def nCallbacks(self, msgname):
# How many callbacks are registered for this message?
        self.lock.acquire()
        try:
            task = self.taskdict[msgname]
        except KeyError:
            return 0
        finally:
            self.lock.release()
        return task.nCallbacks()

    def cnotify(self, oofmessage):
# Messages passed from C++ may be either simple strings or
# OOFMessage instances. The OOFMessage class has swigged
# methods that make it act like a list of PyObjects.
        if isinstance(oofmessage, str):
            self.notify(oofmessage)
        else:
# Convert OOFMessage object into arglist
            self.notify(oofmessage.name(), *tuple(oofmessage))


# Message names passed in from Python need not be strings,
# the only requirement is that they be hashable.  LabelTree,
# for instance, uses (instance, string) pairs as msgnames.
    def requestCallbackMain(self, msgname, func, caller, *args, **kwargs):
        self.lock.acquire()
        try:
            try:
                tasklist = self.taskdict[msgname]
            except KeyError:
                tasklist = self._defineMessage(msgname)
            except TypeError:
                debug.fmsg("Offending message is", msgname)
                raise
            return tasklist.registerCallback(func, caller,
                                             args, kwargs, child_thread = 0)
        finally:
            self.lock.release()

    def requestCallback(self, msgname, func, caller, *args, **kwargs):
        self.lock.acquire()
        try:
            try:
                tasklist = self.taskdict[msgname]
            except KeyError:
                tasklist = self._defineMessage(msgname)
            return tasklist.registerCallback(func, caller,
                                             args, kwargs, child_thread = 1)
        finally:
            self.lock.release()


# Removes a callback from the tasklist, and removes the tasklist
# from the taskdict if its list has become empty.
    def removeCallback(self, action):
        self.lock.acquire()
        try:
            task = self.taskdict[action.msgname]
            task.removeCallback(action)
            if len(task.callbacks)==0:
#                 debug.fmsg("Removing last callback for", action.msgname)
                del self.taskdict[action.msgname]
        finally:
            self.lock.release()
# Switchboard entries for block and unblock -- in most cases,
# it's probably clearer to just locally block and unblock
# the SwitchboardCallback (returned from the requestCallback)
# directly, but these entry points are available for full
# generality.
#   Should catch KeyError and do something sensible?
    def blockCallback(self, action):
        self.lock.acquire()
        try:
            self.taskdict[action.msgname].block()
        finally:
            self.lock.release()
    def unblockCallback(self, action):
        self.lock.acquire()
        try:
            self.taskdict[action.msgname].unblock()
        finally:
            self.lock.release()


switchboard = _Switchboard()
init_switchboard_api(switchboard.cnotify)

def requestCallback(msgname, func, *args, **kwargs):
    caller = debug.callerID()
    return switchboard.requestCallback(msgname, func, caller, *args, **kwargs)

def notify(msgname, *args, **kwargs):
    if switchboard.verbose and switchboard.nCallbacks(msgname) > 0:
# debug.dumpTrace(-6, -2)
        debug.dumpCaller()
    switchboard.notify(msgname, *args, **kwargs)

def removeCallback(action):
    switchboard.removeCallback(action)

def removeCallbacks(actions):
    for action in actions:
        switchboard.removeCallback(action)

def requestCallbackMain(msgname, func, *args, **kwargs):
    caller = debug.callerID()
    return switchboard.requestCallbackMain(msgname, func, caller,
                                           *args, **kwargs)

def verbose(menuitem=None, flag=True):
    old = switchboard.verbose
    switchboard.verbose = flag
    return old



