# Copyright (c) 2000-2001 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program 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.
#
# This program 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
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
__revision__ = "$Id: MemoryController.py,v 1.41 2002/01/03 17:29:21 syt Exp $"

from narval.PlanListener import *
from narval.MemoryListener import *
from HornModel import HornModel
from MemoryCodes import *

from narval.lib import *
from narval.gui import unicode_to_gtk
from NarvalDTD import NarvalDTD

class MemoryController(MemoryListenerInterface,PlanListenerInterface):
    """
    Memory Controller using borg pattern
    """

    _DICT_ = {}

    def __init__(self):
        self.__dict__ = self._DICT_

        if not self.__dict__ :
            # and some data
            self._recipe_edit_windows = {}
            self._plan_monitor_windows = {}
            self._element_edit_windows = {}
            # monitor plans variable
            self.monitor_plans = FALSE
            from MemoryModel import MemoryModel
            self.mmodel = MemoryModel()
            from HornActioner import HornActioner
            self.hactioner = HornActioner()

    ### PUBLIC ###

    # Implement PlanListener Interface #########################################
    def fireTransition(self,planId,transitionId):
        pass

    def transitionStatusChanged(self,planId,transitionId,status):
        self.mmodel.transition_changed(planId,transitionId,status)

    def stepStatusChanged(self,planId,stepId,status):
        self.mmodel.step_changed(planId,stepId,status)

    def planStatusChanged(self,planId,status):
        self.mmodel.plan_changed(planId,status)
        try:
            # should we close the plan window when it ends ?
            from PrefView import PrefView
            if status == 'done' and self._plan_monitor_windows.has_key(planId) \
               and PrefView().plan_button.get_active():
                win = self._plan_monitor_windows[planId]
                del self._plan_monitor_windows[planId]
                win.exit()
        except ImportError:
            pass

    def instanciateRecipe(self,planId,recipeName,planNode=None):
        if self.monitor_plans and not self._plan_monitor_windows.has_key(planId) :
            if not planNode:
                try:
                    planNode = self.mmodel.select_by_id(planId)
                except Exception,e:
                    print unicode_to_gtk(_('Unable to add plan Manager (%s)')%str(e))
                    
            try:
                from RecipeWindow import RecipeWindow
                editor = RecipeWindow(planNode,FALSE) # WARN : boîte de Pandore !
                editor.connect('destroy',self._recipe_edit_window_destroyed,planId)
                editor.show_all()
                self._plan_monitor_windows[planId] = editor
            except SelectException:
                print unicode_to_gtk(_("No recipe <%s> in group <%s>") % (recipe_name,recipe_group))

    def planAddElement(self,planId,elementId):
        pass
        
    def planRemoveElement(self,planId,elementId):
        pass


    # Implement MemoryListener Interface #######################################

    def addElement(self,elementId) :
        e_l = self.hactioner.getElementList(elementId)
        if e_l:
            #print 'MC ADD', elementId, e_l
            self.mmodel.add_element(e_l)
        else:
            print unicode_to_gtk(_('I was not able to find element with eid="%s" in Memory!')%elementId)
    
    def removeElement(self,elementId):
        #print 'MC REMOVE', elementId
        self.mmodel.remove_element(elementId)
        #except Exception:
        #    print unicode_to_gtk(_('element %s not found'))%(str(elementId))

    def replaceElement(self, elementId):
        #print 'MC REPLACE', elementId
        self.mmodel.remove_element(elementId)
        e_l = self.hactioner.getElementList(elementId)
        if e_l:
            self.mmodel.add_element(e_l)
        else:
            print unicode_to_gtk(_("WARNING: replaceElement %s with None")%elementId)

    # local memory callbacks ###################################################

    def save_cookbook(self, name):
        from os.path import normpath, join
        path = normpath(join(config.get_home(),'recipes/%s.xml'%name))
        print unicode_to_gtk(_('writing cookbook %s in %s') % (name, path))
        f=open(path,'w')
        f.write(self.mmodel.get_xmls_cookbook(name))
        f.close()

    def save_all_cookbook(self, gtk):
        for cookbook in self.mmodel.get_cookbooks():
            self.save_cookbook(cookbook)

    def remove_element(self, gtk, eid):
        try:
            from HornModel import HornModel
            if HornModel().narval is not None:
                self.hactioner.removeElement(eid)
            else:
                self.mmodel.removeChild(eid)
        except:
            import traceback
            traceback.print_exc()

    def edit_element(self, gtk, eid):
        """
        create element's edition window for element with eid 'id'
        """
        if not self._element_edit_windows.has_key(eid):
            from logilab.xmltools.XmlEditor import XmlEditorDialog
            node = self.mmodel.select_by_id(eid)
            editor = XmlEditorDialog(unicode_to_gtk(_("Edition of %s"))%node.nodeName,\
                                     NarvalDTD().dtd,node)
            editor.add_change_listener(self.element_changed, eid, node)
            editor.connect('destroy', self._element_edit_window_destroyed, eid)
            editor.show_all()
            self._element_edit_windows[eid] = editor
        else:
            _show(self._element_edit_windows[eid])

    def element_changed(self, *args):
        from HornModel import HornModel
        if HornModel().narval is not None:
            self.hactioner.replaceElement(args[0], args[1].as_xml())
        
    def save_recipe(self, gtk, recipe):
        # WARNING: recipe is a dom tree, not an eid
        self.mmodel.save_recipe(recipe)        

    def remove_recipe(self, gtk, eid):
        self.mmodel.remove_recipe(eid)

    def edit_recipe(self, gtk, eid):
        if not self._recipe_edit_windows.has_key(eid):
            from RecipeWindow import RecipeWindow
            editor = RecipeWindow(self.mmodel.select_by_id(eid), TRUE)
            editor.connect('destroy', self._recipe_edit_window_destroyed, eid)
            self._recipe_edit_windows[eid] = editor
        else:
            _show(self._recipe_edit_windows[eid])

    def edit_recipe_from_plan(self, gtk, eid):
        """ edit recipe from a plan """
        plan_l = self.mmodel.get_element_list(eid)
        try:
            recipe = self.mmodel.select('recipe', plan_l[2], plan_l[3])
            r_eid = recipe.getAttributeNS(AL_NS, 'al:eid')
            if not self._recipe_edit_windows.has_key(r_eid):
                from RecipeWindow import RecipeWindow
                editor = RecipeWindow(recipe,TRUE)
                editor.connect('destroy', self._recipe_edit_window_destroyed, eid)
                self._recipe_edit_windows[r_eid] = editor
            else:
                _show(self._recipe_edit_windows[eid])
        except SelectException:
            print unicode_to_gtk(_("No recipe <%s> in group <%s>"))%(recipe_name,recipe_group)      

    def add_plan_to_monitor(self, gtk, eid):
        if not self._plan_monitor_windows.has_key(eid):
            try:
                from RecipeWindow import RecipeWindow
                editor = RecipeWindow(self.mmodel.select_by_id(eid), FALSE)
                editor.connect('destroy', self._recipe_edit_window_destroyed, eid)
                editor.show_all()
                self._plan_monitor_windows[eid] = editor
            except Exception, e:
                import traceback
                traceback.print_exc()
                print unicode_to_gtk(_("cannot monitor plan"))
        else:
            print unicode_to_gtk(_('plan already monitored'))

    def add_plan_from_error(self, gtk, eid):
        if not self._plan_monitor_windows.has_key(eid):
            try:
                plan = select_one(self.memory, '/*/al:plan[@eid="%s"]'%eid)
                from RecipeWindow import RecipeWindow
                editor = RecipeWindow(self.mmodel.select_by_id(eid), FALSE)
                editor.connect("destroy", self._recipe_edit_window_destroyed, eid)
                editor.show_all()
                self._plan_monitor_windows[eid] = editor
            except:
                print unicode_to_gtk(_("cannot monitor plan"))

    def add_recipe_to_cookbook(self,*args):
        if args[-2]:
            self.mmodel.add_recipe_to_cookbook(args[-2],args[-1])
        else:
            self.mmodel.add_recipe_to_cookbook('no name',args[-1])


    ### PRIVATE ###############################################################
        
    def _recipe_edit_window_destroyed(self, gtk, eid):
        if self._recipe_edit_windows.has_key(eid):
            try:
                self._recipe_edit_windows[eid].destroy()
            finally:
                del self._recipe_edit_windows[eid]
        elif self._plan_monitor_windows.has_key(eid):
            try:
                self._plan_monitor_windows[eid].destroy()
            finally:
                del self._plan_monitor_windows[eid]

    def _element_edit_window_destroyed(self, gtk, eid):
        try:
            self._element_edit_windows[eid].destroy()
        finally:
            del self._element_edit_windows[eid]

def _show(window):
    import gtk
    if window.flags(gtk.MAPPED) == 0:
        window.show()
    else:          
        window.get_window().raise_()
        # call map again so dialogs will refresh their contents  
        window.emit('map', window)
