import ompdModule
import imp

class ompd_parallel(object):

        def __init__(self, parallel_handle):
                """ Initializes an ompd_parallel object with the pointer
                to a handle of a parallel region."""
                self.parallel_handle = parallel_handle
                self.threads = {}
                self.itasks = {}
                self.enclosing_parallel_handle = None
                self.enclosing_parallel = False
                self.task_handle = None

        def get_thread_in_parallel(self, thread_num):
                """Obtains thread handles for the threads associated with the
                parallel region specified by parallel_handle."""
                if not thread_num in self.threads:
                        thread_handle = ompdModule.call_ompd_get_thread_in_parallel(self.parallel_handle, thread_num)
                        self.threads[thread_num] = ompd_thread(thread_handle)
                return self.threads[thread_num]

        def get_enclosing_parallel_handle(self):
                """Obtains a parallel handle for the parallel region enclosing
                the parallel region specified by parallel_handle."""
                if not self.enclosing_parallel_handle:
                        self.enclosing_parallel_handle = ompdModule.call_ompd_get_enclosing_parallel_handle(self.parallel_handle)
                return self.enclosing_parallel_handle

        def get_enclosing_parallel(self):
                if not self.enclosing_parallel:
                        self.enclosing_parallel = ompd_parallel(self.get_enclosing_parallel_handle())
                return self.enclosing_parallel

        def get_task_in_parallel(self, thread_num):
                """Obtains handles for the implicit tasks associated with the
                parallel region specified by parallel_handle."""
                if not thread_num in self.itasks:
                        task_handle = ompdModule.call_ompd_get_task_in_parallel(self.parallel_handle, thread_num)
                        self.itasks[thread_num] = ompd_task(task_handle)
                return self.itasks[thread_num]

        def __del__(self):
                """Releases the parallel handle."""
                pass # let capsule destructors do the job

class ompd_task(object):

        def __init__(self, task_handle):
                """Initializes a new ompd_task_handle object and sets the attribute
                to the task handle specified."""
                self.task_handle = task_handle
                self.task_parallel_handle = False
                self.generating_task_handle = False
                self.scheduling_task_handle = False
                self.task_parallel = False
                self.generating_task = False
                self.scheduling_task = False
                self.task_frames = None
                self.task_frame_flags = None

        def get_task_parallel_handle(self):
                """Obtains a task parallel handle for the parallel region enclosing
                the task region specified."""
                if not self.task_parallel_handle:
                        self.task_parallel_handle = ompdModule.call_ompd_get_task_parallel_handle(self.task_handle)
                return self.task_parallel_handle

        def get_task_parallel(self):
                if not self.task_parallel:
                        self.task_parallel = ompd_parallel(self.get_task_parallel_handle())
                return self.task_parallel

        def get_generating_task_handle(self):
                """Obtains the task handle for the task that created the task specified
                by the task handle."""
                if not self.generating_task_handle:
                        self.generating_task_handle = ompdModule.call_ompd_get_generating_task_handle(self.task_handle)
                return self.generating_task_handle

        def get_generating_task(self):
                if not self.generating_task:
                        self.generating_task = ompd_task(ompdModule.call_ompd_get_generating_task_handle(self.task_handle))
                return self.generating_task

        def get_scheduling_task_handle(self):
                """Obtains the task handle for the task that scheduled the task specified."""
                if not self.scheduling_task_handle:
                        self.scheduling_task_handle = ompdModule.call_ompd_get_scheduling_task_handle(self.task_handle)
                return self.scheduling_task_handle

        def get_scheduling_task(self):
                """Returns ompd_task object for the task that scheduled the current task."""
                if not self.scheduling_task:
                        self.scheduling_task = ompd_task(self.get_scheduling_task_handle())
                return self.scheduling_task

        def get_task_function(self):
                """Returns long with address of function entry point."""
                return ompdModule.call_ompd_get_task_function(self.task_handle)

        def get_task_frame_with_flags(self):
                """Returns enter frame address and flag, exit frame address and flag for current task handle."""
                if self.task_frames is None or self.task_frame_flags is None:
                        ret_value = ompdModule.call_ompd_get_task_frame(self.task_handle)
                        if isinstance(ret_value, tuple):
                                self.task_frames = (ret_value[0], ret_value[2])
                                self.task_frame_flags = (ret_value[1], ret_value[3])
                        else:
                                return ret_value
                return (self.task_frames[0], self.task_frame_flags[0], self.task_frames[1], self.task_frame_flags[1])

        def get_task_frame(self):
                """Returns enter and exit frame address for current task handle."""
                if self.task_frames is None:
                        ret_value = ompdModule.call_ompd_get_task_frame(self.task_handle)
                        if isinstance(ret_value, tuple):
                                self.task_frames = (ret_value[0], ret_value[2])
                        else:
                                return ret_value
                return self.task_frames

        def __del__(self):
                """Releases the task handle."""
                pass # let capsule destructors do the job


class ompd_thread(object):

        def __init__(self, thread_handle):
                """Initializes an ompd_thread with the data received from
                GDB."""
                self.thread_handle = thread_handle
                self.parallel_handle = None
                self.task_handle = None
                self.current_task = False
                self.current_parallel = False
                self.thread_id = False

        def get_current_parallel_handle(self):
                """Obtains the parallel handle for the parallel region associated with
                the given thread handle."""
                #TODO: invalidate thread objects based on `gdb.event.cont`. This should invalidate all internal state.
                self.parallel_handle = ompdModule.call_ompd_get_curr_parallel_handle(self.thread_handle)
                return self.parallel_handle

        def get_current_parallel(self):
                """Returns parallel object for parallel handle of the parallel region
                associated with the current thread handle."""
                if not self.current_parallel:
                        self.current_parallel = ompd_parallel(self.get_current_parallel_handle())
                return self.current_parallel

        def get_current_task_handle(self):
                """Obtains the task handle for the current task region of the
                given thread."""
                if not self.task_handle:
                        self.task_handle = ompdModule.call_ompd_get_curr_task_handle(self.thread_handle)
                return self.task_handle

        def get_thread_id(self):
                """Obtains the ID for the given thread."""
                if not self.thread_id:
                        self.thread_id = ompdModule.call_ompd_get_thread_id(self.thread_handle)
                return self.thread_id

        def get_current_task(self):
                """Returns task object for task handle of the current task region."""
                if not self.current_task:
                        self.current_task = ompd_task(self.get_current_task_handle())
                return self.current_task

        def get_state(self):
                """Returns tuple with OMPD state (long) and wait_id, in case the thread is in a
                waiting state. Helper function for 'ompd threads' command."""
                (state, wait_id) = ompdModule.call_ompd_get_state(self.thread_handle)
                return (state, wait_id)

        def __del__(self):
                """Releases the given thread handle."""
                pass # let capsule destructors do the job
