#!/usr/bin/env python
# 
#   Copyright 2016 RIFT.IO Inc
#
#   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.
#
#
# @file rwcal_status.py
# @brief This module defines Python utilities for dealing with rwcalstatus codes.

import traceback
import functools
import gi
gi.require_version('RwTypes', '1.0')

from gi.repository import RwTypes, RwCal

def rwcalstatus_from_exc_map(exc_map):
    """ Creates an rwcalstatus decorator from a dictionary mapping exception
    types to rwstatus codes, and return a error object containing Exception details
    """

    # A decorator that maps a Python exception to a particular return code.
    # Also returns an object containing the error msg, traceback and rwstatus
    # Automatically returns RW_SUCCESS when no Python exception was thrown.
    # Prevents us from having to use try: except: handlers around every function call.

    def rwstatus(arg=None, ret_on_failure=None):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwds):
                rwcal_status = RwCal.RwcalStatus()
                try:
                    ret = func(*args, **kwds)

                except Exception as e:
                    rwcal_status.traceback = traceback.format_exc()
                    rwcal_status.error_msg = str(e)

                    ret_code = [status for exc, status in exc_map.items() if isinstance(e, exc)]
                    ret_list = [None] if ret_on_failure is None else list(ret_on_failure)
                    if len(ret_code):
                        rwcal_status.status = ret_code[0]
                    else:
                        # If it was not explicitly mapped, print the full traceback as this
                        # is not an anticipated error.
                        traceback.print_exc()
                        rwcal_status.status = RwTypes.RwStatus.FAILURE

                    ret_list.insert(0, rwcal_status)
                    return tuple(ret_list)


                rwcal_status.status = RwTypes.RwStatus.SUCCESS
                rwcal_status.traceback = ""
                rwcal_status.error_msg = ""
                ret_list = [rwcal_status]
                if ret is not None:
                    if type(ret) == tuple:
                        ret_list.extend(ret)
                    else:
                        ret_list.append(ret)

                return tuple(ret_list)

            return wrapper

        if isinstance(arg, dict):
            exc_map.update(arg)
            return decorator
        elif ret_on_failure is not None:
            return decorator
        else:
            return decorator(arg)

    return rwstatus
