# -*- coding: utf-8 -*- 

# Copyright (c) 2007 The PIDA Project

#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:

#The above copyright notice and this permission notice shall be included in
#all copies or substantial portions of the Software.

#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.


# Standard library imports
import os, sys, time, Queue, cgi
import pkgutil

# GTK imports
import gtk, gobject

# kiwi imports
from kiwi.ui.objectlist import ObjectList, ObjectTree, Column
from kiwi.ui.dialogs import open as open_dlg

# PIDA Imports
from pida.core.service import Service
from pida.core.features import FeaturesConfig
from pida.core.commands import CommandsConfig
from pida.core.events import EventsConfig
from pida.core.actions import ActionsConfig
from pida.core.options import OptionsConfig
from pida.core.actions import (TYPE_NORMAL, TYPE_MENUTOOL, TYPE_RADIO, 
                               TYPE_TOGGLE, TYPE_REMEMBER_TOGGLE)
from pida.core.environment import get_pixmap_path

from pida.ui.views import PidaView
from pida.ui.terminal import PidaTerminal
from pida.utils.gthreads import AsyncTask

#from pida.utils import rpdb2
import rpdb2

# locale
from pida.core.locale import Locale
locale = Locale('python_debugger')
_ = locale.gettext


# rpdb2 overrides to force PIDA terminal use


class SessionManagerInternal(rpdb2.CSessionManagerInternal):


    

    def _spawn_server(self, fchdir, ExpandedFilename, args, rid):
        """
        Start an OS console to act as server.
        What it does is to start rpdb again in a new console in server only mode.
        """
        debugger = os.path.abspath(rpdb2.__file__)
        if debugger[-1] == 'c':
            debugger = debugger[:-1]
        baseargs = ['python', debugger, '--debugee', '--rid=%s' % rid]
        if fchdir:
            baseargs.append('--chdir')
        if not self.m_fAllowUnencrypted:
            baseargs.append('--encrypted')
        #if self.m_fRemote:
        #    baseargs.append('--remote')
        if os.name == 'nt':
            baseargs.append('--pwd=%s' % self.m_pwd)
        if 'PGD_DEBUG' in os.environ:
            baseargs.append('--debug')
        baseargs.append(ExpandedFilename)
        cmdargs = baseargs + args.split()
        python_exec = sys.executable
        self.terminal.fork_command(python_exec, cmdargs)



class SessionManager(rpdb2.CSessionManager):

    def __init__(self, manager, pwd, fAllowUnencrypted, fAllowRemote, host):
        self.manager = manager
        smi = self._CSessionManager__smi = SessionManagerInternal(
                            pwd, 
                            fAllowUnencrypted, 
                            fAllowRemote, 
                            host
                            )
        smi.terminal = self

    def _create_view(self):
        view = Terminal(self.app)
        self.main_window.attach_slave('outterm_holder', view)
        return view

    def fork_command(self, *args, **kw):
        def _launch(commandargs=args[1]):
            self.manager.svc.boss.cmd('commander', 'execute',
                commandargs=commandargs)
        gobject.idle_add(_launch)


class DebuggerManager(object):
    """Control the debugging process and views"""
    
    def __init__(self, svc):
        self.svc = svc
        rpdb2.main(self.start_client, argv=[])
        self.last_step = None
        self.connect_events()
        sm = self.session_manager
        self.locals_view = LocalsViewer(sm)
        self.globals_view = GlobalsViewer(sm)
        self.stack_view = StackViewer(sm)
        self.threads_view = ThreadsViewer(sm)
        self.breaks_view = BreakpointViewer(sm)
        self.breaks_view.manager = self
        self.console_view = DebuggerConsole(sm)
        self.terminal_view = PidaTerminal()

    def start_client(self, command_line, fAttach, fchdir, pwd, fAllowUnencrypted, fRemote, host):
        self.session_manager = SessionManager(self, pwd, fAllowUnencrypted, fRemote, host)

    def launch(self, commandline, change_directory=False):
        t = AsyncTask(self.session_manager.launch)
        t.start(change_directory, commandline)

    def connect_events(self):
        event_type_dict = {rpdb2.CEventState: {}}
        self.session_manager.register_callback(self.on_update_state, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventStackFrameChange: {}}
        self.session_manager.register_callback(self.on_update_frame, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventThreads: {}}
        self.session_manager.register_callback(self.on_update_threads, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventNoThreads: {}}
        self.session_manager.register_callback(self.on_update_no_threads, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventNamespace: {}}
        self.session_manager.register_callback(self.on_update_namespace, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventThreadBroken: {}}
        self.session_manager.register_callback(self.on_update_thread_broken, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventStack: {}}
        self.session_manager.register_callback(self.on_update_stack, event_type_dict, fSingleUse = False)
        event_type_dict = {rpdb2.CEventBreakpoint: {}}
        self.session_manager.register_callback(self.on_update_bp, event_type_dict, fSingleUse = False)

    def on_update_state(self, event):
        def update():
            self.console_view.write_info(event.m_state + '\n')
            self.svc.update_state(event.m_state)
        gobject.idle_add(update)

    def on_update_frame(self, event):
        print 'uf', dir(event)
        gobject.idle_add(self.stack_view.select_frame, event.m_frame_index)
        self.on_update_source(event.m_frame_index)

    def on_update_namespace(self, *args):
        gobject.idle_add(self.locals_view.update_namespace)
        gobject.idle_add(self.globals_view.update_namespace)

    def on_update_stack(self, event):
        self.last_stack = event.m_stack
        gobject.idle_add(self.stack_view.update_stack, event.m_stack)
        self.on_update_source(-1)

    def on_update_source(self, frame_index):
        def update():
            self.remove_last_step_mark()
            stack_item = self.last_stack['stack'][frame_index]
            filename, linenumber, level, code = stack_item
            self.svc.boss.cmd('buffer', 'open_file', file_name=filename)
            self.svc.boss.editor.cmd('goto_line', line=linenumber)
            self.svc.boss.editor.cmd('show_sign', type='step', 
                                                    file_name=filename,
                                                    line=linenumber)
            self.last_step = filename, linenumber
        stack_item = self.last_stack['stack'][frame_index]
        if 'rpdb2.py' not in stack_item[0]:
            gobject.idle_add(update)

    def remove_last_step_mark(self):
        if self.last_step is not None:
            lfile, lline = self.last_step
            self.svc.boss.editor.cmd('hide_sign', type='step', file_name=lfile, line=lline) 
            self.last_step = None
            

    def on_update_bp(self, event):
        def _u(event):
            act = event.m_action
            if event.m_bp is not None:
                filename = event.m_bp.m_filename
                linenumber = event.m_bp.m_lineno
                index = event.m_bp.m_id
                indices = None
            else:
                filename = None
                linenumber = None
                index = None
                indices = event.m_id_list
            self.breaks_view.update_bp(act, index, indices, filename, linenumber)
            #self.master.update_bp(act, index, indices, filename, linenumber)
        gobject.idle_add(_u, event)

    def on_update_no_threads(self, *args):
        print 'unt', args

    def on_update_threads(self, event):
        gobject.idle_add(self.threads_view.update_threads, event.m_thread_list, event.m_current_thread)

    def on_update_thread_broken(self, *args):
        print 'utb', args

    def set_breakpoint(self, index, filename, linenumber):
        print 'set', index, filename, linenumber
        self.svc.boss.editor.cmd('show_sign', type='breakpoint', 
                                          file_name=filename,
                                          line=linenumber)

    def remove_breakpoint(self, index, filename, linenumber):
        print 'remove', index, filename, linenumber
        self.svc.boss.editor.cmd('hide_sign', type='breakpoint', 
                                          file_name=filename,
                                          line=linenumber)


# Views
class DebuggerConsole(gtk.VBox):

    def __init__(self, sm):
        gtk.VBox.__init__(self)
        self.sm = sm
        self.console = rpdb2.CConsole(sm, self, self, True)
        self.console.start()
        self._queue = Queue.Queue()
        self._text = gtk.TextView()
        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        sw.add(self._text)
        self._buffer = self._text.get_buffer()
        self._buffer.create_tag('mono', family='Monospace')
        self._buffer.create_tag('bold', weight=800)
        self.pack_start(sw)
        self._entry = gtk.Entry()
        self.pack_start(self._entry, expand=False)
        self._entry.connect('activate', self.on_entry__activate)

    def write_text(self, data, *tags):
        self._buffer.insert_with_tags_by_name(
            self._buffer.get_end_iter(), data, 'mono', *tags)
        self._text.scroll_to_iter(self._buffer.get_end_iter(), 0.1)

    def write_info(self, data):
        gobject.idle_add(self.write_text, '[S] ' + data, 'bold')

    def write_from(self, data):
        gobject.idle_add(self.write_text, data)

    def write_to(self, data):
        if len(data) > 1:
            data = '>>> ' + data
            
        gobject.idle_add(self.write_text, data, 'bold')

    def write(self, data):
        self.write_from(data)

    def flush(self):
        pass

    def readline(self):
        return self._queue.get()

    def on_entry__activate(self, entry):
        data = self._entry.get_text() + '\n'
        self._entry.set_text('')
        self.write_to(data)
        self._queue.put(data)


nochildren = ['NoneType', 'str', 'int', 'float', 'long', 'bool']

reprable = nochildren + ['dict', 'list', 'tuple']


class NamespaceItem(object):

    def __init__(self, nsdict):
        self.name = nsdict['name']
        self.stype = nsdict['type']
        self.srepr = nsdict['repr']
        self.expr = nsdict['expr']
        self.n_subnodes = nsdict['n_subnodes']
        self.key = self.name
        self.is_value = False
        self.expanded = False
    
    def get_markup(self):
        if self.is_value:
            self.name = '.'
            mu = cgi.escape(self.srepr)
        else:
            n = cgi.escape(self.name)
            t = cgi.escape(self.stype)
            mu = ('<tt><b>%s</b>  </tt>'
                  '<span color="#903030"><i><small>%s</small></i></span>'
                  % (n, t))
            if self.stype in reprable:
                v = '<tt> %s</tt>' % cgi.escape(self.srepr)
                mu = ''.join([mu, v])
        return mu
    markup = property(get_markup)

    def get_pixbuf(self):
        if self.is_value:
            return None
        return get_pixbuf(self.stype)
    pixbuf = property(get_pixbuf)


class NamespaceViewer(gtk.VBox):

    def __init__(self, sm):
        gtk.VBox.__init__(self)
        self.session_manager = sm
        self._tree = ObjectTree(
            [
                Column('markup', use_markup=True)
            ]
        )
        self._tree.set_headers_visible(False)
        self.pack_start(self._tree)
        self._tree.connect('row-expanded', self.on_tree__row_expanded)
        
    def update_namespace(self, expr=None, parent=None):
        if expr is None:
            expr = self.get_root_expr()
            parent = None
            self._tree.clear()
        el = [(expr, True)]
        filt = 0
        ns = self.session_manager.get_namespace(el, filt)
        for sn in ns[0]['subnodes']:
            item = NamespaceItem(sn)
            piter = self._tree.append(parent, item)
            if item.stype not in nochildren:
                valitem = NamespaceItem(sn)
                valitem.is_value = True
                self._tree.append(item, valitem)

    def on_tree__row_expanded(self, tv, item):
        if not item.expanded:
            item.expanded = True
            self.update_namespace(item.expr, item)

    def get_root_expr(self):
        raise NotImplementedError


class GlobalsViewer(NamespaceViewer):

    def get_root_expr(self):
        return 'globals()'


class LocalsViewer(NamespaceViewer):

    def get_root_expr(self):
        return 'locals()'




class StackItem(object):

    def __init__(self, index, filename, linenumber, function, line):
        self.key = index
        self.filename = filename
        self.basename = os.path.basename(filename)
        self.dirname = os.path.dirname(filename)
        self.linenumber = linenumber
        self.function = function
        self.line = line
        self.active=False


    def get_markup(self):
        return ('<span color="%(color)s">'
                '<b>%(basename)s:%(linenumber)s</b> '
                '<i><small>%(dirname)s</small></i>\n'
                '<tt>%(line)s</tt></span>' % dict(color=self.color,
                basename=self.basename, linenumber=self.linenumber,
                dirname=self.dirname, line=self.line))
    markup = property(get_markup)

    def get_color(self):
        if self.active:
            return '#000000'
        else:
            return '#909090'
    color = property(get_color)

    def get_icon(self):
        if self.active:
            return None#icons.get(gtk.STOCK_EXECUTE, 16)
        else:
            return None

    pixbuf = property(get_icon)


class StackViewer(gtk.VBox):

    def __init__(self, sm):
        self.session_manager = sm
        gtk.VBox.__init__(self)
        self.tree = ObjectList(
            [
                Column('markup', use_markup=True)
            ]
        )
        self.tree.set_headers_visible(False)
        self.pack_start(self.tree)
        self.tree.connect('double-click', self.on_tree__double_clicked)


    def update_stack(self, stack):
        self._current_tid = stack['current tid']
        self.tree.clear()
        stack_item = None
        for i, row in enumerate(stack['stack'][3:]):
            fn, ln, fc, tl = row
            stack_item = StackItem(i, fn, ln, fc, tl)
            self.tree.insert(0, stack_item)
        if stack_item is not None:
            stack_item.active = True
            self.tree.update(stack_item)
    
    def select_frame(self, index):
        for item in self.tree:
            item.active = (item.key == index)
            self.tree.update(item)

    def on_tree__double_clicked(self, tv, item):
        index = item.key
        self.session_manager.set_frame_index(index)


class ThreadItem(object):

    def __init__(self, tdict):
        self.tid = tdict[rpdb2.DICT_KEY_TID]
        self.broken = tdict[rpdb2.DICT_KEY_BROKEN]
        self.is_current = False
        self.key = self.tid

    def get_broken_text(self):
        if self.broken:
            return 'broken'
        else:
            return 'running'
    state = property(get_broken_text)

    def get_pixbuf(self):
        return None
    pixbuf = property(get_pixbuf)

    def get_markup(self):
        return  ('<tt>%(tid)s</tt> <span color="#909090"><i>%(state)s</i></span>'
           % dict(tid=self.tid, state=self.state) 
        )

    markup = property(get_markup)


class ThreadsViewer(gtk.VBox):

    def __init__(self, sm):
        gtk.VBox.__init__(self)
        self.sesion_manager = sm
        self.tree = ObjectList(
            [
                Column('markup', use_markup=True),
            ]
        )
        self.pack_start(self.tree)
        self.tree.set_headers_visible(False)

    def update_threads(self, threads_list, current_thread):
        self.tree.clear()
        for tdict in threads_list:
            item = ThreadItem(tdict)
            if item.tid == current_thread:
                item.is_current = True
            self.tree.append(item)

    def broken_thread(self, tid):
        for item in self.tree:
            if item.tid == tid:
                item.broken = True
                self.tree.update(item)

class Breakpoint(object):

    def __init__(self, index, filename, linenumber):
        self.key = index
        self.filename = filename
        self.linenumber = linenumber
        self.enabled = True

    def get_color(self):
        if self.enabled:
            return '#000000'
        else:
            return '#a0a0a0'

    def get_disabled_text(self):
        if self.enabled:
            return ''
        else:
            return '(disabled)'

    disabled_text = property(get_disabled_text)
    
    color = property(get_color)

    def get_markup(self):
        return ('<b>[%(key)s]</b>'
                '<span color="%(color)s">'
                '<tt> %(filename)s:%(linenumber)s </tt>'
                '</span><i>%(disabled_text)s</i>') % dict(
                    key=self.key, filename=self.filename,
                    disabled_text=self.disabled_text, color=self.color,
                    linenumber=self.linenumber,
                )

    markup = property(get_markup)


class BreakpointViewer(gtk.VBox):

    def __init__(self, sm):
        self.session_manager = sm
        gtk.VBox.__init__(self)
        self.tree = ObjectList(
            [
                Column('markup', use_markup=True),
            ]
        )
        self.tree.set_headers_visible(False)
        self.pack_start(self.tree)
        #self._create_actions()

    def update_bp(self, action, index, indices, filename, linenumber):
        if action == 'set':
            gen = self._get_all_bps([index])
            try:
                val = gen.next()
                val.filename = filename
                val.linenumber = linenumber
                self.tree.update(val)
            except StopIteration:
                bp = Breakpoint(index, filename, linenumber)
                self.tree.append(bp)
                self.manager.set_breakpoint(index, filename, linenumber)
        elif action == 'remove':
            for item in self._get_all_index_rows(indices):
                filename = item.filename
                self.manager.remove_breakpoint(item.key, item.filename, item.linenumber)
                self.tree.remove(item)
        elif action == 'disable':
            for item in self._get_all_bps(indices):
                item.enabled = False
                self.tree.update(item)
        elif action == 'enable':
            for item in self._get_all_bps(indices):
                item.enabled = True
                self.tree.update(item)


    def _create_actions(self):
        self._current = None
        self.add_widget('dis_act', gtk.Action('Disable', 'Disable',
            'Disable this breakpoint', gtk.STOCK_NO))
        self.add_widget('en_act', gtk.Action('Enable', 'Enable',
            'Enable this breakpoint', gtk.STOCK_YES))

    def _create_popup(self, bp, event):
        self._current = bp
        if not bp: return
        menu = gtk.Menu()
        mi = self.dis_act.create_menu_item()
        menu.add(mi)
        self.dis_act.set_sensitive(bp.enabled)
        mi = self.en_act.create_menu_item()
        menu.add(mi)
        self.en_act.set_sensitive(not bp.enabled)
        menu.show_all()
        menu.popup(None, None, None, event.button, event.time)

    def _get_all_index_rows(self, indices):
        for index in indices:
            for item in self.tree:
                if item.key == index:
                    yield item
        
    def _get_all_bps(self, indices):
        for item in self._get_all_index_rows(indices):
            yield item

    def _set_breakpoint_enabled_status(self, bp, enabled):
        if not enabled:
            func = self.session_manager.disable_breakpoint
        else:
            func = self.session_manager.enable_breakpoint
        gobject.idle_add(func, [bp.key], False)

    def on_tree__double_clicked(self, tv, item):
        if item:
            val = item.value
            self._set_breakpoint_enabled_status(val, not val.enabled)

    def on_tree__right_clicked(self, tv, item, event):
        if item is None:
            val = item
        else:
            val = item.value
        self._create_popup(val, event)
        
    def on_dis_act__activate(self, action):
        print 'disabled'
        self._set_breakpoint_enabled_status(self._current, False)

    def on_en_act__activate(self, action):
        self._set_breakpoint_enabled_status(self._current, True)

# PIDA View

class PythonDebuggerView(PidaView):
    key = 'pythondebugger.shell'

    icon_name = 'python-icon'
    label_text = _('Python Debugger')

    def create_ui(self):
        self.manager = self.svc.debugger_manager
        self.create_toolbar()
        nb1 = gtk.Notebook()
        nb1.append_page(self.manager.console_view, gtk.Label('Console'))
        #nb1.append_page(self.manager.terminal_view, gtk.Label('Execution Output'))
        nb1.append_page(self.manager.breaks_view, gtk.Label('Break Points'))
        hb = gtk.HBox()
        hb.pack_start(nb1)
        hb.pack_start(self._toolbar, expand=False)
        self.add_main_widget(hb)
        hb.show_all()

    def create_toolbar(self):
        self._uim = gtk.UIManager()
        self._uim.insert_action_group(self.svc.get_action_group(), 0)
        uidef = pkgutil.get_data(__name__, 'uidef/python-debugger-toolbar.xml')
        self._uim.add_ui_from_string(uidef)
        self._uim.ensure_update()
        self._toolbar = self._uim.get_toplevels('toolbar')[0]
        self._toolbar.set_style(gtk.TOOLBAR_ICONS)
        self._toolbar.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR)
        self._toolbar.set_orientation(gtk.ORIENTATION_VERTICAL)
        self._toolbar.show_all()

class PythonDebuggerView2(PidaView):
    key = 'pythondebugger.stacks'

    icon_name = 'python-icon'
    label_text = _('Python Debugger')


    def create_ui(self):
        self.manager = self.svc.debugger_manager
        nb2 = gtk.Notebook()
        nb2.append_page(self.manager.stack_view, gtk.Label('Stack'))
        nb2.append_page(self.manager.threads_view, gtk.Label('Threads'))
        nb2.append_page(self.manager.locals_view, gtk.Label('Locals'))
        nb2.append_page(self.manager.globals_view, gtk.Label('Globals'))
        self.add_main_widget(nb2)
        nb2.show_all()


class PythondebuggerCommandlineView(PidaView):

    key = 'pythondebugger.launcher'

    gladefile = 'pythondebugger_commandline'

    icon_name = 'gtk-execute'
    label_text = _('Debug Python Script')



    def on_exec_button__clicked(self, button):
        commandline = self.command_line.get_text()
        if commandline:
            self.svc.launch(commandline, self.change_directory.get_active())

    def on_open_button__clicked(self, button):
        fn = open_dlg('Select a script to load.', parent=self.svc.boss.window)
        if fn:
            self.command_line.set_text(fn)



# Service Configuration

# Actions
class DebuggerActionsConfig(ActionsConfig):
    def create_actions(self):
        # Menu
        self.create_action(
            'show_pydebugger_view',
            TYPE_REMEMBER_TOGGLE,
            'Show Python Debugger',
            'Show the Python debugger',
            'accessories-text-editor',
            self.on_show_debugger_view,
            '<Shift><Control>b',
        )

        self.create_action(
            'execute_pythondebugger',
            TYPE_NORMAL,
            'Debug Python Script',
            'Load a python script into the debugger',
            'gtk-execute',
            self.on_exec_commandline,
            '<Shift><Control>6',
        )

        #self.create_action(
        #    'show_stack_view',
        #    TYPE_TOGGLE,
        #    "Debugger's stack view",
        #    'Show the stack of current debugger',
        #    'accessories-text-editor',
        #    self.on_show_stack_view,
        #    '<Shift><Control>s',
        #)
    
        # Toolbar
        self.create_action(
            'debug_start',
            TYPE_NORMAL,
            'Continue',
            'Start debugger or Continue debbuging',
            'gdb-go',
            self.on_start,
            '<F3>',
        )
        #self.create_action(
        #    'debug_stop',
        #    TYPE_NORMAL,
        #    'Break',
        #    'Stop debbuging',
        #    'gdb-break',
        #    self.on_stop,
        #    '<F4>',
        #)
        self.create_action(
            'debug_next',
            TYPE_NORMAL,
            'Next',
            'Step to the next statement',
            'gdb-next',
            self.on_step_over,
            '<F6>',
        )
        self.create_action(
            'debug_step',
            TYPE_NORMAL,
            'Step',
            'Step into highlighted statement',
            'gdb-step',
            self.on_step_in,
            '<F5>',
        )
        self.create_action(
            'debug_return',
            TYPE_NORMAL,
            'Finish function',
            'Step until end of current function',
            'gdb-return',
            self.on_return,
            '<F7>',
        )
        self.create_action(
            'debug_break',
            TYPE_NORMAL,
            'Break',
            'Breakpoint the execution',
            'gdb-break',
            self.on_break,
            '<F3>',
        )

    # Buttonbar
    def on_step_over(self, action):
        self.svc._view.manager.session_manager.request_next()

    def on_step_in(self, action):
        self.svc._view.manager.session_manager.request_step()

    def on_start(self, action):
        self.svc._view.manager.session_manager.request_go()

    def on_stop(self, action):
        self.svc._view.manager.session_manager.stop_debuggee()

    def on_return(self, action):
        self.svc._view.manager.session_manager.request_return()

    # Menu
    def on_show_debugger_view(self, action):
        if action.get_active():
            self.svc.show_debugger_view()
        else:
            self.svc.boss.cmd('window', 'remove_view', view=self.svc._breakpoints_view)

    def on_show_stack_view(self, action):
        if not self.svc._stack_view:
            self.svc._stack_view = DebuggerStackView(self.svc)

        if action.get_active():
            self.svc.boss.cmd('window', 'add_view', paned='Terminal', view=self.svc._stack_view)
        else:
            self.svc.boss.cmd('window', 'remove_view', view=self.svc._stack_view)
        
    #
    def on_break(self, action):
        self.svc._view.manager.session_manager.request_break()

    def on_exec_commandline(self, action):
        self.svc.show_cmdline_dialog()

class DebuggerCommands(CommandsConfig):

    def launch(self, command_line, change_directory=False):
        self.svc.launch(command_line, change_directory)


class DebuggerEventsConfig(EventsConfig):

    def subscribe_all_foreign(self):
        #self.subscribe_foreign('buffer', 'document-changed',
        #                             self.on_document_changed)
        self.subscribe_foreign('editor', 'started',
                                     self.on_editor_startup)

    # XXX Well, that's not gonna work
    def on_editor_startup(self):
        """
        Set the highlights in vim
        """
        self.svc.boss.editor.cmd('define_sign_type', type="breakpoint", icon=get_pixmap_path("stop.svg"), 
                                                linehl="", text="X", texthl="Search")
        self.svc.boss.editor.cmd('define_sign_type', type="step", icon=get_pixmap_path("forward.svg"), 
                                                linehl="lCursor", text=">", texthl="lCursor")

    def on_document_changed(self, document):
        if document is not None:
            self.svc.get_action('debug_toggle_breakpoint').set_sensitive(True)
            self.svc.update_editor(document)
        else:
            self.svc.get_action('debug_toggle_breakpoint').set_sensitive(False)

# Service class
class Python_debugger(Service):
    """Describe your Service Here""" 
    actions_config = DebuggerActionsConfig
    commands_config = DebuggerCommands
    events_config = DebuggerEventsConfig

    def start(self):
        self.debugger_manager = DebuggerManager(self)
        self._view = PythonDebuggerView(self)
        self._view2 = PythonDebuggerView2(self)
        self.set_all_actions_insensitive()

    def show_debugger_view(self):
        self.boss.cmd('window', 'add_view', paned='Terminal', view=self._view)
        self.boss.cmd('window', 'add_view', paned='Plugin', view=self._view2)

    def hide_debugger_view(self):
        self.boss.cmd('window', 'remove_view', view=self._view)
        self.boss.cmd('window', 'remove_view', view=self._view2)

    def ensure_view_visible(self):
        if not self.get_action('show_pydebugger_view').get_active():
            self.get_action('show_pydebugger_view').set_active(True)
        self.boss.cmd('window', 'present_view', view=self._view)

    def launch(self, command_line, change_directory):
        self.ensure_view_visible()
        self._view.manager.launch(command_line, change_directory)

    def update_state(self, state):
        if state == 'broken':
            self.set_broken_actions()
        elif state == 'running':
            self.set_running_actions()
        elif state == 'detached':
            self.set_all_actions_insensitive()
            self._view.manager.remove_last_step_mark()

        else:
            self.set_all_actions_insensitive()

    def set_all_actions_insensitive(self):
        for actname in ['debug_start', 'debug_next', 'debug_break',
                        'debug_step', 'debug_return']:
            self.get_action(actname).set_sensitive(False)

    def set_running_actions(self):
        self.set_all_actions_insensitive()
        for actname in ['debug_break']:
            self.get_action(actname).set_sensitive(True)

    def set_broken_actions(self):
        self.set_all_actions_insensitive()
        for actname in ['debug_start', 'debug_next', 'debug_step',
                        'debug_return']:
            self.get_action(actname).set_sensitive(True)

    def stop(self):
        try:
            self._view.manager.session_manager.shutdown()
            self._view.manager.session_manager.stop_debuggee()
        except:
            pass

    def show_cmdline_dialog(self):
        view = PythondebuggerCommandlineView(self)
        self.boss.cmd('window', 'add_view', paned='Terminal', view=view)


# Required Service attribute for service loading
Service = Python_debugger



# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
