# -*- coding: utf-8 -*-
"""
$Id$

Copyright 2012 Lars Kruse <devel@sumpfralle.de>

This file is part of PyCAM.

PyCAM 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.

PyCAM 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 PyCAM.  If not, see <http://www.gnu.org/licenses/>.
"""


import StringIO
import csv
import gobject
# guppy will be imported later
#import guppy

import pycam.Plugins
import pycam.Utils.log

_log = pycam.Utils.log.get_logger()


class MemoryAnalyzer(pycam.Plugins.PluginBase):

    UI_FILE = "memory_analyzer.ui"
    DEPENDS = ["Clipboard"]
    CATEGORIES = ["System"]

    def setup(self):
        if self.gui:
            import gtk
            self._gtk = gtk
            # menu item and shortcut
            self.toggle_action = self.gui.get_object("ToggleMemoryAnalyzerAction")
            self._gtk_handlers = []
            self._gtk_handlers.append((self.toggle_action, "toggled",
                    self.toggle_window))
            self.register_gtk_accelerator("memory_analyzer", self.toggle_action,
                    None, "ToggleMemoryAnalyzerAction")
            self.core.register_ui("view_menu", "ToggleMemoryAnalyzerAction",
                    self.toggle_action, 80)
            # the window
            self.window = self.gui.get_object("MemoryAnalyzerWindow")
            self.window.set_default_size(500, 400)
            hide_window = lambda *args: self.toggle_window(value=False)
            self._gtk_handlers.extend([
                    (self.window, "delete-event", hide_window),
                    (self.window, "destroy", hide_window),
                    (self.gui.get_object("MemoryAnalyzerCloseButton"),
                            "clicked", hide_window),
                    (self.gui.get_object("MemoryAnalyzerCopyButton"),
                            "clicked", self.copy_to_clipboard),
                    (self.gui.get_object("MemoryAnalyzerRefreshButton"),
                            "clicked", self.refresh_memory_analyzer)])
            self.model = self.gui.get_object("MemoryAnalyzerModel")
            # window state
            self._window_position = None
            # check if "heapy" is available - this disables all widgets
            try:
                import guppy
            except ImportError:
                self._guppy = None
                self.gui.get_object("MemoryAnalyzerDataBox").hide()
            else:
                self._guppy = guppy
                self.gui.get_object("MemoryAnalyzerBrokenLabel").hide()
            self.register_gtk_handlers(self._gtk_handlers)
        return True

    def teardown(self):
        if self.gui:
            self.window.hide()
            self.core.unregister_ui("view_menu", self.toggle_action)
            self.unregister_gtk_accelerator("memory_analyzer",
                    self.toggle_action)
            self.core.unregister_ui("view_menu", self.toggle_action)
            self.unregister_gtk_handlers(self._gtk_handlers)

    def toggle_window(self, widget=None, value=None, action=None):
        checkbox_state = self.toggle_action.get_active()
        if value is None:
            new_state = checkbox_state
        elif action is None:
            new_state = value
        else:
            new_state = action
        if new_state:
            if self._window_position:
                self.window.move(*self._window_position)
            self.refresh_memory_analyzer()
            self.window.show()
        else:
            self._window_position = self.window.get_position()
            self.window.hide()
        self.toggle_action.set_active(new_state)
        # don't destroy the window with a "destroy" event
        return True

    def refresh_memory_analyzer(self, widget=None):
        self.model.clear()
        self.gui.get_object("MemoryAnalyzerLoadingLabel").show()
        for objname in ("MemoryAnalyzerRefreshButton",
                "MemoryAnalyzerCopyButton"):
            self.gui.get_object(objname).set_sensitive(False)
        gobject.idle_add(self._refresh_data_in_background)

    def _refresh_data_in_background(self):
        if not self._guppy:
            return
        memory_state = self._guppy.hpy().heap()
        for row in memory_state.stat.get_rows():
            item = (row.name, row.count, row.size / 1024, row.size / row.count)
            self.model.append(item)
        for objname in ("MemoryAnalyzerRefreshButton",
                "MemoryAnalyzerCopyButton"):
            self.gui.get_object(objname).set_sensitive(True)
        self.gui.get_object("MemoryAnalyzerRefreshButton").set_sensitive(True)
        self.gui.get_object("MemoryAnalyzerLoadingLabel").hide()

    def copy_to_clipboard(self, widget=None):
        text_buffer = StringIO.StringIO()
        writer = csv.writer(text_buffer)
        writer.writerow(("Type", "Count", "Size (all) [kB]", "Average size [B]"))
        for row in self.model:
            writer.writerow(row)
        self.core.get("clipboard-set")(text_buffer.getvalue())
        text_buffer.close()

