# src/ScriptAdapter.py - Copyright 2005, 2006, University
#                            of Padova, dept. of Pure and Applied
#                            Mathematics
#
# This file is part of SGPEMv2.
#
# This 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 3 of the License, or
# (at your option) any later version.
#
# SGPEMv2 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 SGPEMv2. If not, see http://www.gnu.org/licenses/.


import sys, mutex, thread
import sgpem

## @brief This is an adapter class which acts as a proxy
# for user-implemented policies
#
# At runtime, this class will be initialized with the
# user-implemented policy, and then it will proxy the calls
# the policy member functions on a different thread, so
# to ensure asyncronous control.
#
# Instantiated in the C++ code, it is created like:
# @code
# adapter = ScriptAdapter(UserPolicyClass)
# @endcode
#
# @remarks The user shouldn't care about this class at all.
class ScriptAdapter :
    ## @var The policy this ScriptAdapter will use for calls
    _policy = None

    ## @var Synchronized return value you can read from C++
    # when a threaded function returns
    _ret_val = None

    ## @var Testable syncronization object
    _g_mutex = mutex.mutex()

    ## @var The exception raised from the last called user-defined 
    # method. It's value is \c None if no exception were raised
    _g_last_exception = None

    ## @brief Constructor of ScriptAdapter
    #
    # @param self The caller object
    # @param policy A user-implemented class inheriting from Policy.Policy
    def __init__(self, policy):
        self._policy = policy()
        print ('ScriptAdapter for policy ', policy, ' loaded')

    ## @brief Asynchronously call Policy.configure()
    #
    # @param self The caller object
    def async_configure(self):
        self._g_last_exception = None
        self._g_mutex.lock(ScriptAdapter._wrap_configure, self )
        
    def _wrap_configure(self):
        thread.start_new_thread(ScriptAdapter._wrap_configure_callback, (self,))
        
    def _wrap_configure_callback(self):
        # call configure method
        try:
            self._policy.configure()
        except:
            self._g_last_exception = sys.exc_value
        self._g_mutex.unlock()


    ## @brief Asynchronously call Policy.sort_queue()
    #
    # The queue is asked directly to the C++ sgpem::Scheduler
    # singleton, via SWIG
    #
    # @param self The caller object
    def async_sort_queue(self):
        self._g_last_exception = None
        self._g_mutex.lock(ScriptAdapter._wrap_sort_queue, self)

    def _wrap_sort_queue(self):
        thread.start_new_thread(ScriptAdapter._wrap_sort_queue_callback, (self,))

    def _wrap_sort_queue_callback(self):
        # here we retrieve and pass the ready queue
        queue = sgpem.Scheduler.get_instance().get_ready_queue()
        
        try:
            self._policy.sort_queue(queue)
        except:
            # exception raised in user-defined method,
            # save it so the C++ code can tell the
            # user what went wrong
            self._g_last_exception = sys.exc_value
        self._g_mutex.unlock()


    ## @brief Asynchronously call Policy.is_preemptive()
    #
    # @param self The caller object
    def async_is_preemptive(self):
        self._g_last_exception = None
        self._g_mutex.lock(ScriptAdapter._wrap_is_preemptive, self)

    def _wrap_is_preemptive(self):
        thread.start_new_thread(ScriptAdapter._wrap_is_preemptive_callback, (self,))

    def _wrap_is_preemptive_callback(self):
        try:
            self._ret_val = self._policy.is_preemptive()
        except:
            # exception raised in user-defined method,
            # save it so the C++ code can tell the
            # user what went wrong
            self._g_last_exception = sys.exc_value
        self._g_mutex.unlock()
    
    ## @brief Asynchronously call Policy.get_time_slice()
    #
    # @param self The caller object
    def async_get_time_slice(self):
        self._g_last_exception = None
        self._g_mutex.lock(ScriptAdapter._wrap_get_time_slice, self)

    def _wrap_get_time_slice(self):
        thread.start_new_thread(ScriptAdapter._wrap_get_time_slice_callback, (self,))

    def _wrap_get_time_slice_callback(self):
        try:
            self._ret_val = self._policy.get_time_slice()
        except:
            # exception raised in user-defined method,
            # save it so the C++ code can tell the
            # user what went wrong
            self._g_last_exception = sys.exc_value
        self._g_mutex.unlock()
    
    ## @brief Return the global shared variable with the methods' last return value
    def get_return_value(self):
        return self._ret_val

    def mutex_test_lock(self):
        return self._g_mutex.test()
    
    def get_last_exception(self):
        return self._g_last_exception

