# Copyright (C) 2003, 2004 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de> (2003)
# Jan-Oliver Wagner <jan@intevation.de> (2003, 2004)
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

"""Performance Measurement

This module implements two Thuban commands in a new Profiling menu:

   Profile Screen Render -- Run the screen rendering code in a profile

   Time Screen Render -- Measure the time taken for a complete redraw

See the individual functions for more details.
"""

__version__ = "$Revision: 2721 $"
# $Source$
# $Id: profiling.py 2721 2007-01-13 15:11:42Z dpinte $

import os
import StringIO
import sys
import tempfile
import profile
import time
import pstats

from wx.lib.dialogs import ScrolledMessageDialog

from Thuban import _
from Thuban.UI.command import registry, Command
from Thuban.UI.mainwindow import main_menu

#
# Customization
#
# Assign to these in your ~/.thuban/thubanstart

# The machine specific profiler bias. See the standard python profile
# module for details on how to find out which value to use.
profiler_bias = 0

# The directory the profile output is to be written to
# (Call mktemp once to initialize tempfile.tempdir)
tempfile.mktemp()
profile_dir = tempfile.tempdir

# Wether to pop up a dialog box with the result.
popup_dialog_box = True


#
#       Timing and profiling a complete redraw
#

def do_redraw(context):
    """Perform a complete redraw in the canvas in context"""
    canvas = context.mainwindow.canvas

    # Make sure there are no no finished bitmaps and active renderer
    canvas.full_redraw()

    # Iterate until all is drawn
    for c in canvas._render_iterator():
        pass


#
# Profiling the redraw
#


def profile_screen_renderer(context):
    """Script to run the redraw in the profiler

    The data gathered by the profiler will be written to
    <TMPDIR>/thuban-render.profile (<TMPDIR> is your system specific
    temporary directory.

    See the python documentation of the profile and pstats modules for
    how to access the data in the generated .profile file.
    """
    print "profiling screen renderer...",
    sys.stdout.flush()
    prof = profile.Profile(bias = profiler_bias)
    prof.runctx("do_redraw(context)", globals(), locals())
    filename = os.path.join(profile_dir, "thuban-render.profile")
    prof.dump_stats(filename)
    print "done and saved to", filename

    if popup_dialog_box:
        # catch the printout to stdout so that we can present the
        # text in a dialog
        f = StringIO.StringIO()
        orig_stdout = sys.stdout
        sys.stdout = f
        try:
            p = pstats.Stats(filename)
            msg = _('These are the statistics sorted by cumulative time:')
            p.strip_dirs().sort_stats('cumulative').print_stats()
            m = f.getvalue()
            msg = '%s\n\n%s' % (msg, m)
        finally:
            sys.stdout = orig_stdout

        dlg = ScrolledMessageDialog(context.mainwindow, msg,
                                      _('Profile Screen Render'))
        dlg.ShowModal()


registry.Add(Command("profile_screen_renderer", _('Profile Screen Render'),
                     profile_screen_renderer,
                     helptext = _('Profile the screen render')))


#
# Timing the redraw
#

def time_screen_renderer(context):
    """Script to measure the time of a complete redraw.

    The time taken will be printed to stdout.
    """
    start = time.clock()
    do_redraw(context)
    duration = time.clock() - start
    msg = _('Redraw finished in %g seconds.') % duration
    if popup_dialog_box:
        context.mainwindow.RunMessageBox(_('Time Screen Render'), msg)
    else:
        print msg


registry.Add(Command("time_screen_renderer", _('Time Screen Render'),
                     time_screen_renderer,
                     helptext = _('Time the screen render')))

# find the extensions menu (create it anew if not found)
extensions_menu = main_menu.FindOrInsertMenu('extensions', _('E&xtensions'))

profiler_menu = extensions_menu.InsertMenu("profiler", _('&Profiler'))
profiler_menu.InsertItem("time_screen_renderer")
profiler_menu.InsertItem("profile_screen_renderer")
