#file: safety.py
#Copyright (C) 2008 FunnyMan3595
#This file is part of Endgame: Singularity.

#Endgame: Singularity 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 2 of the License, or
#(at your option) any later version.

#Endgame: Singularity 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 Endgame: Singularity; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#This file contains wrapper functions for making error-tolerant "safe" calls.

import logging

import pygame
import time
import traceback
import sys

class Buffer(object):
    def __init__(self, prefix=""):
        self.data = prefix
    def write(self, unbuffered):
        self.data += unbuffered

def get_timestamp(when=None):
    if when == None:
        when = time.time()
    return time.ctime(when) + " " + time.tzname[time.daylight]


def log_error(error_message, *args):
    if len(args):
        sys.stderr.write((error_message % args) + "\n")
    else:
        sys.stderr.write(error_message + "\n")
    if len(logging.getLogger().handlers) > 0:
        try:
            logging.getLogger().error(error_message, *args)
        except IOError:  # Probably access denied with --singledir. That's ok
            pass


def log_func_exc(func):
    buffer = Buffer("Exception in function %s at %s:\n```\n"
                                   % (func.__name__, get_timestamp()))
    traceback.print_exc(file=buffer)
    buffer.write("```")
    log_error(buffer.data)


FIRST_ERROR = True

def safe_call(func, args=(), kwargs={}, on_error=None):
    try:
        return func(*args, **kwargs)
    except Exception:
        global FIRST_ERROR
        if FIRST_ERROR:
            log_error("----- Basic information (Please include all the data below in the bug report) ------")
            log_error("Please submit the crash on github: https://github.com/singularity/singularity/issues/new")
            try:
                from singularity import __full_version__
            except ImportError:
                __full_version__ = "N/A (Import error)"
            log_error("Singularity version %s", __full_version__)
            log_error("Python version %s", sys.version.replace("\n", ''))
            log_error("Pygame version %s", pygame.version.ver)
            log_error("SDL version %s", ".".join(str(x) for x in pygame.get_sdl_version()))
            FIRST_ERROR = False
        log_func_exc(func)

#        # ... --- ...
#        import g
#        g.play_sound("click")
#        delays = (.15, .15, .8, .5, .5, .8, .15, .15)
#        for delay in delays:
#            time.sleep(delay)
#            g.play_sound("click")

        return on_error

# Catches any errors raised by a function, logs them, and returns the given
# value.
#
# Apply to a function like so:
# @safe(my_error_code)
# def my_function(...)
#
# And then:
# result = my_function(...)
# if result == my_error_code:
#     # An error was raised.
def safe(on_error):
    return lambda func: _safe(func, on_error)

def _safe(func, on_error):
    return lambda *args, **kwargs: safe_call(func, args, kwargs, on_error)
