import string


## CLASSE FILE.DESC:
## =================
# Decrit et decode un fichier 3MU

class FileDesc:

  ## METHODE __INIT__:
  ## -----------------
  # Cree une instance par decodage d'un fichier
  # fileName = nom du fichier a decoder
  
  def __init__(self, fileName):
    ## Creation du dictionnaire des caracteristiques
    self.carDict = {}
    ## Ouverture du fichier
    f = open(fileName, 'r')
    ## On teste la premiere ligne
    l = f.readline()
    if l <> '#!3MU\n': raise TypeError, "File %s is not a 3MU file" % fileName
    ## Lecture du fichier
    tag = self.getNextTag(f)
    while tag <> None:
      val = self.getNextVal(f)
      self.carDict[tag] = val
      tag = self.getNextTag(f)
    ## Fini
    f.close()
  
  
  ## METHODE __GETITEM__:
  ## --------------------
  # Lit une caracteristique lue dans le fichier
  
  def __getitem__(self, key):
    return self.carDict[key]
    
    
  ## METHODE KEYS:
  ## -------------
  # Rend l'ensemble des caracteristiques lues dans le fichier
  
  def keys(self):
    return self.carDict.keys()
    
    
  ## METHODE HAS_KEY:
  ## ----------------
  # Teste la presence d'une caracteristique donne dans le fichier
  
  def has_key(self, key):
    return self.carDict.has_key(key)
    
    
  ## METHODE GET.NEXT.CHAR:
  ## ----------------------
  # Lit le prochain caractere dans un fichier en consommant les commentaires
  # file = fichier a lire
  
  def getNextChar(self, file):
    ## Lecture du caractere
    c = file.read(1)
    ## Est-ce un debut de commentaire ?
    if c == '!':
      ## Lecture jusqu'a la fin de la ligne
      while c <> '\n':
        c = file.read(1)
        if c == '': return None
      ## Un step de plus et on y est
      c = file.read(1)
    ## Si EOF, c'est fini
    if c == '': return None
    ## Sinon, on rend le caractere lu
    return c
    

  ## METHODE GET.NEXT.NON.BLANK.CHAR:
  ## --------------------------------
  # Lit le prochain caractere dans un fichier en consommant les commentaires et les blancs
  # file = fichier a lire
  
  def getNextNonBlankChar(self, file):
    ## Lecture
    c = self.getNextChar(file)
    ## Si c'est fini, c'est fini
    if c == None: return None
    ## Est-ce un blanc ?
    if c in string.whitespace:
      ## Lecture de tous les blancs
      while c in string.whitespace:
        c = self.getNextChar(file)
        if c == None: return None
      ## On revient d'un petit coup en arriere
      file.seek(-1, 1)
      ## Et on rend un blanc
      return ' '
    ## Sinon, on rend le caractere trouve
    return c
    
    
  ## METHODE GET.NEXT.TAG:
  ## ---------------------
  # Lit le nom de parametre suivant dans le fichier
  
  def getNextTag(self, f):
    ## Lecture du premier caractere
    c = self.getNextNonBlankChar(f)
    ## Si c'est un blanc, on le saute
    if c == ' ': c = self.getNextNonBlankChar(f)
    ## Si c'est fini, c'est fini
    if c == None: return None
    ## Construction du nom du tag
    tagName = string.lower(c)
    c = self.getNextNonBlankChar(f)
    ## Lecture jusqu'au '='
    while c <> '=':
      ## Si fin de fichier, erreur
      if c == None: raise TypeError, "Found blank in tag name beginning with %s" % tagName
      ## Si blanc, le '=' doit etre derriere
      if c == ' ':
        c = self.getNextNonBlankChar(f)
        if c <> '=': raise TypeError, "Found blank in tag name beginning with %s" % tagName
      ## Si non '=', on ajoute au nom du tag
      if c <> '=': tagName = tagName + string.lower(c)
      ## Caractere suivant
      c = self.getNextNonBlankChar(f)
    ## Fini, on rend le nom construit
    return tagName
    
    
  ## METHODE GET.NEXT.VAL:
  ## ---------------------
  # Lit la valeur de parametre suivante dans le fichier
  
  def getNextVal(self, f):
    ## Lecture du premier caractere
    c = self.getNextNonBlankChar(f)
    ## Si c'est un blanc, on le saute
    if c == ' ': c = self.getNextNonBlankChar(f)
    ## Si c'est fini, erreur
    if c == None: raise TypeError, "Unexpected end of file in value"
    ## Est-ce un debut de parametre multi-value ?
    if c == '{':
      ## Lecture jusqu'a la fin
      strVal = ''
      c = self.getNextNonBlankChar(f)
      if c == None: raise TypeError, "Unexpected end of file in value"
      while c <> '}':
        strVal = strVal + c
        c = self.getNextNonBlankChar(f)
        if c == None: raise TypeError, "Unexpected end of file in value"
      ## Construction de la liste (en minuscules)
      val = string.split(string.lower(strVal))
    ## Si valeur simple
    else:
      ## Lecture jusqu'au blanc ou a la fin de fichier
      if c in string.whitespace: c = self.getNextNonBlankChar(f)
      if c == None: raise TypeError, "Unexpected end of file in value"
      val = c
      c = self.getNextNonBlankChar(f)
      while c <> None and not c in string.whitespace:
        val = val + string.lower(c)
        c = self.getNextNonBlankChar(f)
    ## Fini: on rend la valeur en minuscules
    return val
