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

"""RecipeLayout module.

Library of algorithms that compute the layout of the recipe/plan graph.
"""

__revision__ = "$Id: RecipeLayout.py,v 1.11 2002/01/10 17:01:30 syt Exp $"

from narval.lib import *

class RecipeLayout :
    """Basic layout"""

    def _prepare_layout(self, recipe) :
        # make list of following elements in "flow order"
        self.start_step, self.end_step = None, None
        self.followers = followers = {}
        steps_io = {}
        for step in select_many(recipe, 'al:step'):
            id = step.getAttributeNS(NO_NS,'id')
            followers[id], steps_io[id] = [], ([],[])
        for transition in select_many(recipe, 'al:transition'):
            id = transition.getAttributeNS(NO_NS,'id')
            followers[id] = []
            for inref in select_many(transition, 'al:in') :
                ref = inref.getAttributeNS(NO_NS,'idref')
                followers[ref].append(id)
                steps_io[ref][1].append(id)
            for outref in select_many(transition, 'al:out') :
                ref = outref.getAttributeNS(NO_NS,'idref')
                followers[id].append(ref)
                steps_io[ref][0].append(id)
        for id in steps_io.keys():
            if len(steps_io[id][0]) == 0:
                self.start_step = id
            if len(steps_io[id][1]) == 0:
                self.end_step = id
        # keep references
        self.recipe = recipe
        self.line = {}
        self.column = {}
        self.zero()

    def zero(self):
        for element in self.followers.keys() :
            self.line[element] = 0
            self.column[element] = 0

    def set_line(self,element,line):
        if element and self.line.has_key(element):
            if self.line[element] < line :
                self.line[element] = line
            for follower in self.followers[element] :
                self.set_line(follower,line+1)

    def set_column(self,element,column) :
        if element and self.column.has_key(element):
            self.column[element] = column
            for follower in self.followers[element] :
                if self.column[follower] == 0 :
                    column = self.set_column(follower,column) + 1
            if self.column[element] < column :
                column = column - 1
            return column

    def layout(self,recipe) :
        self._prepare_layout(recipe)
        self.set_line(self.start_step,1)
        self.set_column(self.start_step,1)
        return self.line, self.column

class RecipeLayout1(RecipeLayout) :
    """Layout type 1"""

    def set_column(self,element,col_dict,parent_col):
        if element and self.column.has_key(element):
            l = self.line[element]
            self.column[element] = max(col_dict.get(l,0)+1,parent_col)
            col_dict[l] = self.column[element]
            for follower in self.followers[element] :
                if not self.column[follower] :
                    self.set_column(follower,col_dict,self.column[element])
    
    def layout(self,recipe) :
        self._prepare_layout(recipe)
        self.set_line(self.start_step,1)
        self.set_column(self.start_step,{},1)
        return self.line, self.column

class RecipeLayout2(RecipeLayout) :
    """Layout type 2"""

    def set_column(self,element,col_dict) :
        if element and self.column.has_key(element):
            l = self.line[element]
            col_dict[l] = col_dict.get(l,0) + 1
            self.column[element] = col_dict[l]
            for follower in self.followers[element] :
                if not self.column[follower] :
                    self.set_column(follower,col_dict)
    
    def layout(self,recipe) :
        self._prepare_layout(recipe)
        self.set_line(self.start_step,1)
        self.set_column(self.start_step,{})
        return self.line, self.column

class RecipeLayout3(RecipeLayout2) :
    """Layout type 3"""

    def layout(self,recipe) :
        self._prepare_layout(recipe)
        self.set_line(self.start_step,1)
        self.set_column(self.start_step,{})
        max_line = max(self.line.values())
        for i in range(max_line) :
            elements = []
            for element in self.line.keys():
                if self.line[element] == i :
                    elements.append(element)
            dist, max_dist = {}, None
            for element in elements :
                for f in self.followers[element] :
                    d = abs(self.column[f]-self.column[element])
                    if d > dist[element][0] :
                        dist[element] = d, f
                        if max_dist and dist[max_dist] < d :
                            max_dist = d
        # to be continued...
        return self.line, self.column

