"""
1999 Manuel Gutierrez Algaba
You are free to use , modify, distribute and copy this piece of
python code, if you keep this free copyright notice in it.

grapher.py Version 1.0

This small programm produces state diagrams, just specifying the
relationship among the different states

"""

class graphstate:
   def __init__(self, label1, label2 = None):
       self.label1= label1
       self.label2 = label2
       self.next = None
       self.goto=[]
       self.label_next = None
       self.make_width()
       self.size = ""
       self.numberlabel = None
       self.figure = "ellipse"
       self.already_drawn = 0
       self.already_drawn_arrows = 0
       self.index_of_goto_arrows = 0

   def do_size(self, s):
       """
       valid sizes are: 0.9 and 0.8, or greater
       """
       self.size = s

   def rel(self,n=None, l= None, f = None, g=[] ):
       """
       This makes a relationship among this state and those stated
       in n and g.
       n = the next one state,
       g = a list of states where this state goes
       l = label of the transition of this state to the next one
       f = forced_next , this forces f to be the next node to be visited.
       """
       self.next = n
       self.label_next = l
       self.goto = g
       self.forced_next = f

   def make_width(self):
       if self.label2 is None:
           t = len(self.label1)
       else:
           t = max(len(self.label1), len(self.label2))

       self.width = t

   def generate_gpic_code(self, file ):
       f = open( file, "w")
       self.write_headings(f)
       self.write_body(f)
       self.write_footnote(f)
       f.close()

   def make_size(self, size ):
       self.size = size

   def write_headings(self, f):
       if self.size=="":
           f.write(".PS \ndown\narrowhead=7\narrow\n")
       else:
           f.write(".PS "+`self.size`+"\ndown\narrowhead=7\narrow\n")

   def write_footnote(self, f):
       f.write(".PE\n")

   def write_body(self , f, numberlabel = None):
       self.assign_numberlabel()
       self.start_drawing(f)
       self.arrows_and_such(f)

   def start_drawing(self,f):
       print self.numberlabel, self.already_drawn
       if self.already_drawn == 1:
           return
       self.already_drawn = 1
       self.write_label(f)
       self.write_enclosing_figure(f)
       self.write_movement(f)
       self.write_rest_of_it(f)

   def write_rest_of_it(self,f):
       if not self.forced_next is None:
           self.forced_next.start_drawing(f)

       if not self.next is None:
           self.next.start_drawing(f)

       for i in self.goto:
           i[0].start_drawing(f)

   def write_rest_of_it_arrows(self,f):
       if not self.next is None:
           self.next.arrows_and_such(f)

       for i in self.goto:
           i[0].arrows_and_such(f)

   def write_movement(self,f):
       if self.label_next is None:
           f.write("move 0.2\n")
       else:
           f.write("move 0.5\n")

   def write_enclosing_figure(self, f ):
       if self.label2 is None:
           part1 = self.figure+  ' "'+self.label1  + '" '
       else:
           part1 = self.figure+ ' "'+ self.label1 + '" "' + self.label2 + '" '

           #print "line 200",self.width
       part2 = " ht 0.5 wid " + str( 0.12 *  self.width )
       f.write(part1 + part2 + "\n")

   def convert_numberlabel(self):
       return self.numberlabel[0]+str(self.numberlabel[1])

   def write_label(self,f):
       f.write(self.convert_numberlabel() +":  ")

   def what_numberlabel(self):
       return self.numberlabel

   def assign_numberlabel(self, numberlabel= None):
       """
       We go through all the states in the drawing. And we label them.
       """
       if not self.numberlabel is None:
           return
       # first we assign our own numberlabel
       if numberlabel is None:
           self.numberlabel = ('L', 0)
       else:
           #print "line 224",self.numberlabel
           self.numberlabel = ('L', numberlabel + 1)
       # then we propagate the numberlabels
       propagated_numberlabel = self.numberlabel[1]

       if not self.forced_next is None:
           propagated_numberlabel = self.forced_next.assign_numberlabel( propagated_numberlabel)

       if not self.next is None:
           propagated_numberlabel = self.next.assign_numberlabel( propagated_numberlabel)

       for i in self.goto:
           if i[0].what_numberlabel() is None:
               propagated_numberlabel = i[0].assign_numberlabel(propagated_numberlabel)

       return propagated_numberlabel

   def arrows_and_such(self, f):
       if self.already_drawn_arrows == 1:
           return
       self.already_drawn_arrows = 1
       self.write_arrows(f)
       self.write_rest_of_it_arrows(f)

   def write_arrows(self, file):
       '''
       Cases:
       arrow next:
         arrow   "health" rjust from Gairst.s to Econd.n
         arrow  from Econd.s to Erd.n
       arrow self:
          PAPER : spline    from Erd.e then up 0.2 right  0.3 then down 0.4 then up 0.2 left 0.3 ->
          box invis  "apteros"  at PAPER + ( 0.6, 0.0)
       arrow goto:
          D: arc ->  from Erd.e to Gairst.e
          sprintf("zoom") ljust at D.e + (0.2, 0.0)

       '''
       self.write_arrow_next(file)
       self.write_arrow_goto(file)

   def write_arrow_next(self, f):
       if not self.forced_next is None:
           self.arrow_forced_west((self.next, self.label_next),f )
           return

       if not self.next is None:
           if not self.label_next is None:
               f.write('arrow "'+self.label_next+'" rjust from '+self.convert_numberlabel()+'.s to '+ self.next.convert_numberlabel()+'.n\n')
           else:
               f.write('arrow from '+self.convert_numberlabel()+'.s to '+ self.next.convert_numberlabel()+'.n\n')

   def write_arrow_goto(self,f):
       '''
       arrow goto down to up:
          D: arc ->  from Erd.e to Gairst.e
          sprintf("zoom") ljust at D.e + (0.2, 0.0)
       arrow goto up to down :
          D: arc ->  from Erd.w to Gairst.w
          sprintf("zoom") rjust at D.e + (-0.2, 0.0)

       '''
       for i in self.goto:
           if i[0] != self:
               self.make_arrow(i,f)
           else:
               self.write_arrow_self(i,f)

   def make_arrow(self,arrow,f):
       if  self.is_lower(arrow[0]):
           self.arrow_down_up(arrow,f)
       else:
           self.arrow_up_down(arrow,f)

   def arrow_down_up(self, arrow, f):
       intlabel = self.generate_goto_arrow_internal_label(f)
       self.generate_arc(arrow,f,"DOWN-UP")
       self.generate_goto_arrow_label(arrow,f,intlabel,"DOWN-UP")

   def arrow_up_down(self, arrow, f):
       intlabel = self.generate_goto_arrow_internal_label(f)
       self.generate_arc(arrow,f,"UP-DOWN")
       self.generate_goto_arrow_label(arrow,f,intlabel,"UP-DOWN")
   def arrow_forced_west(self, arrow, f):
       intlabel = self.generate_goto_arrow_internal_label(f)
       self.generate_arc(arrow,f,"FORCED-WEST")
       self.generate_goto_arrow_label(arrow,f,intlabel,"FORCED-WEST")

   def generate_goto_arrow_internal_label(self, f):
       self.index_of_goto_arrows = self.index_of_goto_arrows + 1
       to_return = self.convert_numberlabel()+`self.index_of_goto_arrows`+" :"
       f.write(to_return)
       return to_return

   def generate_arc(self, arrow, f, location):
       if location =="DOWN-UP":
           f.write('arc -> from '+self.convert_numberlabel()+'.w to '+ arrow[0].convert_numberlabel()+'.w\n')
       elif location =="UP-DOWN":
           f.write('arc -> from '+self.convert_numberlabel()+'.e to '+ arrow[0].convert_numberlabel()+'.e\n')
       elif location=="FORCED-WEST":
           f.write('arc -> from '+self.convert_numberlabel()+'.w to '+ arrow[0].convert_numberlabel()+'.w\n')

   def generate_goto_arrow_label(self, arrow,f,intlabel,location):
       if arrow[1] is None:
           return
       if location =="DOWN-UP":
           #print "line 163", `arrow[1]`, intlabel
           f.write('sprintf("'+arrow[1]+'") ljust at '+intlabel[:-2]+'.w +(0.2, 0.0)\n')
       elif location =="UP-DOWN":
           f.write('sprintf("'+arrow[1]+'") rjust at '+intlabel[:-2]+'.e +(-0.2, 0.0)\n')
       elif location=="FORCED-WEST":
           f.write('sprintf("'+arrow[1]+'") ljust at '+intlabel[:-2]+'.w +(0.2, 0.0)\n')



   def is_lower(self, another_state):
       return self.what_numberlabel()[1] < another_state.what_numberlabel()[1]

   def write_arrow_self(self, arrow, f):
       '''
       PAPER : spline    from Erd.e then up 0.2 right  0.3 then down 0.4 then up 0.2 left 0.3 ->
       box invis  "apteros"  at PAPER + ( 0.6, 0.0)
       '''
       intlabel = self.generate_goto_arrow_internal_label(f)
       if arrow[1] is None:
           label= ""
       else:
           label = arrow[1]

       f.write(' spline from '+ self.convert_numberlabel()+'.e then up 0.2 right  0.3 then down 0.4 then up 0.2 left 0.3 -> \n \
box invis  "'+label +'" at '+ intlabel[:-2] + " +( 0.6, 0.0)\n")


def gs(label1 = None, label2 = None):
   return graphstate(label1,label2)


def __test24__():

   st1 = graphstate("wake up")
   st2 = graphstate("breakfast")
   st3 = graphstate("homework", "done?")
   st4 = graphstate("nice ", "class day")
   st5 = graphstate("bad", "class day")
   st6 = graphstate("have lunch")
   st7 = graphstate("go to","party")
   st8 = graphstate("have fun")
   st9 = graphstate("next day")
   #st1.do_size(2)
   st1.rel(n=st2)
   st2.rel(n=st3)
   st3.rel(n=st4, l='Yes', g=[(st5,'No')])
   st4.rel(n=st6,f=st5)
   st5.rel(n=st6)
   st6.rel(n=st7)
   st7.rel(n=st8)
   st8.rel(n=st9,g=[(st8, "have fun")])
   st9.rel(g=[(st1,"Sleep")])

   st1.generate_gpic_code("test1.gpic")

def __test25__():

   st1 = graphstate("consideraciones metafisicas up")
   st2 = graphstate("kuntakinte en norte america")
   st3 = graphstate("burros y otros", "el burro espa'nol")

   st1.rel(n=st2)
   st2.rel(n=st3)
   st3.rel(g=[(st1,"hola"),(st2,"adios")])

   st1.generate_gpic_code("test1.gpic")

if __name__=='__main__':
   __test24__()