#!/usr/bin/env python

# lernen findet nur bei einer falschen antwort statt, die dann in eine frage mit 2 antworten umgewandelt wird.
# Danksagungen:
#   irc::nomis@irc.freenode.net : del lines[0]  um in lines ein Element zu loeschen. Erspart mir die gloable Variable.

import re

# Offenbar brauche ich zum Einlesen der Wissensdatenbank eine globale Variable:

comment = """

  tree = node()
  tree.set_is_answer( 'ein Baum' )

  app.run:
     ...
     tree: answer: J: "Ist es <ein Brombeerbusch>?" 
                              N: - "Was ist es denn?" "ein Baum"
                                 - "Welche Frage unterscheidet <ein Brombeerbusch> und <ein Baum>?": Hat es einen dicken Stamm?
                                 -  Mit welcher Antwort ergibt "Hat es einen dicken Stamm? j/n" "ein Baum"?

                           (Q)         (Q)
                             \    ->     \
                             (A)         (Q2)
                                         /  \
                                       (A)  (B)  bzw andersrum



                        (Q)
                        / \
                      (Q) (Q)
                      /\   /\
                    (A)(A)(A)(Q)
                             / \
                           (A) (A)

"""
# --------------------------------------------------------------------------
class node (object):

   """Ein Node ist eine Antwort, oder eine Verzweigung anhand einer j/n-Frage."""
   def __init__(self):
      self.parent = None
      self.text = None

   def setText(self, text):
      self.text = text

   def getText(self):
      return self.text

   def setParent(self, ref):
      self.parent = ref

# --------------------------------------------------------------------------
class answer (node):
   def __init__(self, text, is_loading=False, parent=None, lines=None ):
      node.__init__(self)
      node.setText(self, text)
      
      # recursive rebuilding from configuration file:
      if is_loading:
         data = lines[0]
         # print "rebuilding answer", data
         self.setParent(parent)
         del lines[0] # got text already, delete this line

   def insert_question(self, new_object_question, new_object_name, new_object_is_yes):
      """diese instanz plus die neue sind die beiden antworten der neuen frage"""
      q = question(new_object_question)
      q.setParent(self.parent)

      a2 = answer(new_object_name)
      a2.setParent(q)

      if new_object_is_yes:
         q.setChildYes( a2 )
         q.setChildNo( self )
      else:
         q.setChildYes( self )
         q.setChildNo( a2 )

      # nun noch dem Parent das neue Child geben:
      if self.parent != None:
         if self.parent.answered_yes: # Wenn die Vorfrage durch JA hierhin fuehrte, muessen wir dort einklinken:
            self.parent.setChildYes(q)
         else:
            self.parent.setChildNo(q) # bzw auf der anderen seite

      # jemand anders ist nun der elter dieser instanz:
      self.parent = q
   
   def dump(self, depth=0):
      print "%3d A: %s" % (depth, self.text)

   def dumpToString(self, depth=0):
      return "%3d A: %s\n" % (depth, self.text)

# --------------------------------------------------------------------------
class question (node):
   def __init__(self, text, is_loading=False, parent=None, lines=None ):

      node.__init__(self)
      node.setText(self, text)
      self.child_yes = None
      self.child_no  = None
      self.answered_yes = False  # Antwort des Users ist False (nein) oder True (ja)

      # recursive rebuilding from configuration file:
      if is_loading:
         data = lines[0] # got text already
         # print "rebuilding a question:", data
         self.setParent(parent)
         del lines[0] # remove my data from the lines
         # left child:
         # print "left child:"
         if lines[0]['QA'] == 'Q':
            self.child_no = question( lines[0]['text'], True, self, lines )  # changes lines data
         else:
            self.child_no = answer( lines[0]['text'], True, self, lines ) 
         
         # right child:
         # print "right child:"
         if lines[0]['QA'] == 'Q':
            self.child_yes = question( lines[0]['text'], True, self, lines ) # changes lines data
         else:
            self.child_yes = answer( lines[0]['text'], True, self, lines ) 


   def setUseranswer(self, truefalse):
      self.answered_yes = truefalse

   def setChildYes(self, child):
      self.child_yes = child
      
   def setChildNo(self, child):
      self.child_no = child
   
   def getNext(self):
      if self.answered_yes:
         return self.child_yes
      return self.child_no

   def dump(self, depth=0):
      print "%3d Q: %s" % (depth, self.text)
      if self.child_no != None:
         self.child_no.dump(depth+1)
      if self.child_yes != None:
         self.child_yes.dump(depth+1)

   def dumpToString(self, depth=0):
      s = ''
      s = s + "%3d Q: %s\n" % (depth, self.text)
      if self.child_no != None:
         s = s + self.child_no.dumpToString(depth+1)
      if self.child_yes != None:
         s = s + self.child_yes.dumpToString(depth+1)
      return s


# --------------------------------------------------------------------------
class application:
  def __init__(self):
     self.tree = None
     self.current_node = self.tree

  def _load_lines(self, fname):
     r = re.compile("^\s+(\d+)\s+(Q|A):\s+(.*)")
     lines = []
     for line in open(fname, 'r'):
        m = r.search(line)
        if m != None:
          data = {}
          data['depth'] = m.group(1)
          data['QA']    = m.group(2)
          data['text']  = m.group(3)
          lines.append( data )
     return lines

  def load_knowledge(self, fname):
     lines = self._load_lines(fname)
     # print lines
     # the top element is a question, so we can call the question constructor without thinking:
     self.tree = question( lines[0]['text'], True, None, lines ) # load your childs from lines
     self.current_node = self.tree


  def save_knowledge(self):
     fname = 'wissen.lst'
     f = open( fname, 'w')
     f.write( self.tree.dumpToString() )
     f.close()
     print "Die Daten wurden in Datei '%s' gespeichert." % fname
     # todo save data

  def run(self):
     print
     keep_going = True
     while keep_going:
        keep_going = self.guess_and_learn()

     self.save_knowledge()

  def jn_input(self, text):
     jn = raw_input( text )
     if jn == 'j':
        return True
     return False

  def guess_and_learn(self):
     if isinstance(self.current_node, answer):
        is_guessed_right = self.jn_input("Ist es %s? (j/n) " % (self.current_node.getText(), ))
        if is_guessed_right:
           print "\n\n  J U H U U U !!!\n\n"
        else:
           print "\n  Du hast gewonnen!\n"
           print "Hilf mir bitte, mein Wissen zu erweitern:\n"
           print "Antworte auf die folgende Frage bitte mit etwas"
           print "in der Art von 'ein Baum', 'eine Plage' -- aber bitte ohne Satzzeichen dahinter:\n"
           
           es_ist = raw_input("Was hattest du dir vorgestellt? Es war ")
           
           newquestion = raw_input("\nWelche Frage unterscheidet '%s' und\n'%s' so, dass die Antwort fuer das eine\nJA und fuer das andere NEIN lautet? " % (self.current_node.getText(), es_ist))
           
           newanswer_is_yes = self.jn_input( "\nGilt fuer '%s': '%s'? (j/n) " %  (es_ist, newquestion) )
           self.current_node.insert_question( newquestion, es_ist, newanswer_is_yes )
           
        print "\nAktueller Datenbestand:" 
        self.tree.dump()
        print "\n"

        nochmal = self.jn_input( "Nochmal (j/n)? " )

        if nochmal:
           self.current_node = self.tree
           return True
        return False

     elif isinstance(self.current_node, question):
        # ask current question, save answer in object, go to next object:
        answered_yes = self.jn_input( "Q: %s (j/n) " % (self.current_node.getText(),) )
        self.current_node.setUseranswer( answered_yes )
        self.current_node = self.current_node.getNext()
        return True
     else:
        print "ich habe keine ahnung was ich bin: ", self.current_node


# --------------------------------------------------------------------------
app = application()
app.load_knowledge('wissen.lst')
# app.load_knowledge('meinwissen.dat')
app.run()



