'''
This file is a part of Test Mile Arjuna
Copyright 2018 Test Mile Software Testing Pvt Ltd

Website: www.TestMile.com
Email: support [at] testmile.com
Creator: Rahul Verma

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''

import functools
import threading
import types

from arjuna.core.adv.wrappers import ClassPassThrough


# From blog post by Daniel Holden at http://theorangeduck.com/page/synchronized-python
def sync_function(func):
    # reentrant lock to supprt recursion
    func.__lock__ = threading.RLock()

    @functools.wraps(func)
    def synced_func(*args, **kws):
        with func.__lock__:
            return func(*args, **kws)

    return synced_func


def sync_class(klass):
    for key in klass.__dict__:
        val = klass.__dict__[key]
        if type(val) is types.FunctionType:
            decorator = sync_function
            setattr(klass, key, decorator(val))

    return klass


# From blog post by Daniel Holden at http://theorangeduck.com/page/synchronized-python
# To use the following you should create self.lock = threading.RLock() in __init__
# Per instance basis lock
def sync_method(lock_name):
    def decorator(method):
        @functools.wraps(method)
        def synced_method(self, *args, **kws):
            lock = getattr(self, lock_name)
            with lock:
                return method(self, *args, **kws)

        return synced_method

    return decorator
