#!/usr/bin/env python
# encoding: iso-8859-15
#
#Minë. Mundo Interactivo-Narrativo en Español [Tierra-Media]
#Copyright (C) 2002  Pablo Ruiz Múzquiz
#
#
#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
#
#
#Fichero: Objeto.py

"""Clase Objeto."""

# NOTA: esta clase contiene la última variante del proceso de parsear un fichero XML (con funciones
# aparte para crear una nueva instancia a partir de xml y un constructor que recibe como
# parámetros todos los atributos, y que sólo debe ser llamado desde la propia clase). Cuando tengamos
# tiempo, algún día hay que modificar todas las otras clases para que usen este mismo patrón.

import logging
import random
import sys
import string
import os
import cPickle

from utils import *
import Modulo
import xmlutils

log = logging.getLogger("Objetos")

# Version del sistema de descripciones de objeto (se incrementa cada vez que cambia
# el formato de los ficheros de desc_objetos)
VERSION_DESC_OBJETO = '1.0.1'

# Version del sistema de objetos (se incrementa cada vez que varian los atributos
# de la clase Objeto)
VERSION_OBJETO = '1.0.6'

# Otras constantes globales

DIR_DESC_OBJETOS = 'desc_objetos'
EXT_DESC_OBJETOS = '.xml'

TIPOS_DE_MATERIAL = [ 'madera', 'metal', 'cristal', 'piel', 'tejido', 'piedra',
		      'organico', 'cera', 'liquido', 'barro' ]

CATEGORIAS = [ 'mobiliario', 'arma', 'recipiente', 'joya', 'armadura', 'herramienta',
	       'vestimenta', 'escritura', 'recipìente', 'comestible', 'accesorio' ]

SUCESOS = [ 'personaje_come', 'personaje_bebe', 'personaje_lanza', 'personaje_usa',
	    'personaje_usa_con', 'personaje_pone_cabeza', 'personaje_pone_cuello',
	    'personaje_pone_tronco', 'personaje_pone_brazo_izq', 
	    'personaje_pone_brazo_der', 'personaje_pone_mano_izq', 
	    'personaje_pone_mano_der', 'personaje_pone_dos_manos', 
	    'personaje_pone_piernas', 'personaje_pone_tobillo_izq', 
	    'personaje_pone_tobillo_der', 'personaje_pone_pie_izq', 
	    'personaje_pone_pie_der', 'personaje_quita_cabeza',
	    'personaje_quita_cuello', 'personaje_quita_tronco',
	    'personaje_quita_brazo_izq', 'personaje_quita_brazo_der',
	    'personaje_quita_mano_izq', 'personaje_quita_mano_der',
	    'personaje_quita_dos_manos', 'personaje_quita_piernas',
	    'personaje_quita_tobillo_izq', 'personaje_quita_tobillo_der',
	    'personaje_quita_pie_izq', 'personaje_quita_pie_der' ]


class Objeto(object):
    """Un objeto dentro del juego.

    Al instanciar un objeto, se crea nuevo y se lee desde el fichero de descripcion de objeto,
    poniendolo en su estado inicial. Esta instancia podrá pasar a manos de pjs y pnjs, y se
    puede salvar a disco junto con la sala o el personaje que lo contiene.

    Los ficheros XML descriptores de objetos se encuentran situados debajo del directorio
    DIR_DESC_OBJETOS. Por ejemplo, el descriptor del objeto con id cerveza01 estará en 
    DIR_DESC_OBJETOS/cerveza01.xml. Los objetos guardados con Pickle se guardan en el mismo
    fichero que la sala en la que se encuentren.
    """

    # Atributos públicos de la clase

    def __leer_id(self): return self.__id 

    id = property(__leer_id,
                  doc="""(string): identifica al objeto, pero no unívocamente. Puede haber
		      más de una instancia del objeto compartiendo el id.

		      No puede contener espacios.

		      Invariantes:
			  id != None
		      """)

    def __leer_version(self): return self.__version 

    version = property(__leer_version,
		       doc="""(int): la versión del sistema de objetos.

		           Invariantes:
			       version != None
		 	   """)

    def __leer_autor(self): return self.__autor 

    autor = property(__leer_autor,
		     doc="""autor (string): identifica al autor del objeto.
		     
		         Invariantes:
			     autor != None
			 """)

    def __leer_comentario(self): return self.__comentario 

    comentario = property(__leer_comentario,
			  doc="""(string): un metacomentario acerca del objeto.
			  
			      Invariantes:
				  comentario != None
			      """)
		      
    def __leer_area(self): return self.__area 

    area = property(__leer_area,
	            doc="""(string): el area a la que pertenece el objeto, si es que lo necesita.
		    
		        Invariantes:
			    area != None
		        """)

    def __leer_nombre(self): return self.__nombre 

    nombre = property(__leer_nombre,
		      doc="""(string): Una breve frase informando de qué es el objeto.
		      
		          Invariantes:
			      nombre != None
			  """)

    def __leer_descripcion(self): return self.__descripcion 

    descripcion = property(__leer_descripcion,
			   doc="""({String:any}[]): descripción del objeto.

			       Es una lista de diccionarios, conteniendo cada uno:
			        - "dificultad" (int): de 1 a 100
			        - "texto" (string): el contenido del item de descripcion
				- "personaje_mira" (Modulo): el módulo que se ejecuta cuando
				                             un personaje mira el item
				
			       Invariantes:
				   descripcion != None
				   len(descripcion) >= 0
				   descripcion[i]["dificultad"] in range(0,100)
				   descripcion[i]["texto"] != None
			       """)

    def __leer_tipo(self): return self.__tipo 

    tipo = property(__leer_tipo,
		    doc="""(string): el tipo de material del objeto.
		    
		        Invariantes:
			    tipo != None
			    tipo in TIPOS_DE_MATERIAL
			""")

    def __leer_categoria(self): return self.__categoria 

    categoria = property(__leer_categoria,
			 doc="""(string): el tipo de uso generico del objeto.
			 
			     Invariantes:
				 categoria != None
				 categoria in CATEGORIAS
			     """)

    def __leer_peso(self): return self.__peso 

    peso = property(__leer_peso,
                    doc="""(float): peso en kilos.
		    
		        Invariantes:
			    peso > 0
		        """)

    def __leer_volumen(self): return self.__volumen 

    volumen = property(__leer_volumen,
		       doc="""(float): volumen en relación a 1 unidad de persona.
		       
		           Invariantes:
			       volumen > 0
		 	   """)

    def __leer_cargas(self): return self.__cargas 

    cargas = property(__leer_cargas,
		      doc="""(int): el número de veces que puede ser usado antes de destruirse o
			  volverse inservible. El valor 0 indica que no tiene limite.
			  
			  Invariantes:
			      cargas >= 0
			  """)

    def __leer_valor(self): return self.__valor 

    valor = property(__leer_valor,
		     doc="""(int): en monedas de estaño.
		     
		         Invariantes:
			     valor > 0
			 """)

    def __leer_estado(self): return self.__estado 

    estado = property(__leer_estado,
		      doc="""(int): de 0 a 100.
		      
		          Invariantes:
			      estado in range(0,100)
			  """)

    def __leer_aura(self): return self.__aura 

    aura = property(__leer_aura,
		    doc="""(int): de 0 a 100.
		    
		        Invariantes:
			    aura in range(0,100)
			""")

    def __leer_propietario(self): return self.__propietario 

    propietario = property(__leer_propietario,
			   doc="""(string): el id del personaje que posee este objeto.
			   Este personaje es el único que tiene derecho a usarlo y
			   llevarlo de un lado a otro.

			   Si el propietario es un PNJ, esta propiedad contiene el id
			   sin el '%' ni el código único (el propietario es cualquier
			   instancia de ese PNJ).

			   Puede valer None si no tiene propietario.
			   """)

    def __leer_contadores(self): return self.__contadores

    contadores = property(__leer_contadores,
			  doc="""({string:int}): cada contador es un nombre asociado a
			      un número, que se puede acceder libremente desde guiones.

		              Invariantes:
                                  contadores != None
		              """)

    def __leer_etiquetas(self): return self.__etiquetas

    etiquetas = property(__leer_etiquetas,
			 doc="""({string:string}): cada etiqueta es un nombre asociado a
			     un valor de texto, que se puede acceder libremente desde
			     guiones.

		             Invariantes:
                                 etiquetas != None
		             """)

    def __leer_sucesos(self): return self.__sucesos

    sucesos = property(__leer_sucesos,
                       doc="""({String:Modulo}: respuestas a los sucesos del objeto.

		       El índice del diccionario es el tipo del suceso, y asociado a él va un módulo
		       que se ejecuta como respuesta.

		       Invariantes:
			   sucesos != None
			   sucesos.keys(i) in SUCESOS
		       """)

    def __leer_contenedor(self): return self.__contenedor

    contenedor = property(__leer_contenedor,
			  doc="""(contenedor): entidad que contiene a este objeto.

		          Normalmente será una sala o un personaje. Puede ser None temporalmente,
		          durante alguna operación (por ejemplo, después de cogerlo de una sala y
		          antes de meterlo en la mochila).
		          """)


#    def __leer_usos(self): return self.__usos 
#
#    usos = property(__leer_usos,
#                    doc="""({String:any}[]): lista de usos del objeto.
#
#		        Cada uso es un diccionario:
#			 - "tipo" (string): el tipo de uso ("comer", "poner", "lanzar", etc.)
#			 - "posicion" (string): si el uso es "poner", en que parte del cuerpo se pone.
#			 - "requisitos" (Requisito[]): lista de requisitos para poder descubrir el
#			                               uso o realizarlo.
#			 - "acciones" (Accion[]): lista de acciones que ocurren al realizar este uso.
#
#			Invariantes:
#			    usos != None
#			    len(usos) >= 0
#			    usos[i]["tipo"] in USOS
#			    usos[i]["posicion"] in Personaje.POSICIONES
#			    usos[i]["requisitos"] != None
#			    usos[i]["acciones"] != None
#		        """
##		        """({string:any}[]): lista de usos del objeto.
##
##		        Cada uso es un diccionario:
##			 - "tipo" (string): el tipo de uso ("comer", "poner", "lanzar", etc.)
##			 - "posicion" (string): si el uso es "poner", en que parte del cuerpo se pone
##			 - "gasto" (int): el numero de cargas que consume cada uso
##			 - "msg" (string): el mensaje que aparece cuando se usa
##			 - "duracion" (int): el numero de segundos que dura el efecto (0 = indeterminado)
##			 - "requisitos" ({string:any}[]): lista de requisitos del uso, siendo cada requisito
##							  un diccionario:
##			      - "atributo" (string): el atributo a comprobar
##			      - "valor" (int): el valor mínimo que deberá tener el atributo
##			      - "msg" (string): el mensaje que sale si el atributo no llega al mínimo
##			 - "efectos" ({string:any}[]): lista de efectos que produce el uso, siendo cada
##						       efecto un diccionario:
##			      - "atributo" (string): el atributo a modificar
##			      - "valor" (int): el número de puntos que sube o baja (si es negativo)
##			      - "duracion" (int): duración del efecto en segundos (0 = ilimitado)
##			      - "msg" (string): el mensaje que sale cuando ocurre el efecto
##			      
##			Invariantes:
##			    usos != None
##			    len(usos) >= 0
##			    usos[i]["tipo"] in USOS
##			    usos[i]["posicion"] in Personaje.POSICIONES
##			    usos[i]["gasto"] >= 0
##			    usos[i]["msg"] != None
##			    usos[i]["duracion"] >= 0
##			    usos[i]["requisitos"] != None
##			    usos[i]["requisitos"][j]["atributo"] != None
##			    usos[i]["requisitos"][j]["valor"] in range(0,100)
##			    usos[i]["requisitos"][j]["msg"] != None
##			    usos[i]["efectos"] != None
##			    usos[i]["efectos"][k]["atributo"] != None
##			    usos[i]["efectos"][k]["duracion"] >= 0
##			    usos[i]["efectos"][k]["msg"] != None
##			""")


    # Constructor

    def __init__(self, id, version, autor, comentario, area, nombre, descripcion, tipo,
	         categoria, peso, volumen, cargas, valor, estado, aura, propietario,
		 contadores, etiquetas, sucesos, max_cargas, fecha_fichero, contenedor):
	"""Crea un nuevo objeto a partir de sus atributos.

	Requiere:
	    Los parámetros satisfacen las invariantes de los atributos.
	"""

	# Atributos privados:
	#
	# __max_cargas (int): valor inicial de la propiedad cargas, nunca podrá superar este valor
        # __fecha_fichero (int): fecha de la ultima modificacion del fichero de descripción
	#
	# Invariantes:
        #   __fecha_fichero >= 0

        self.__id = id
	self.__version = version
	self.__autor = autor
	self.__comentario = comentario
	self.__area = area
	self.__nombre = nombre
	self.__descripcion = descripcion
	self.__tipo = tipo
	self.__categoria = categoria
	self.__peso = peso
	self.__volumen = volumen
	self.__cargas = cargas
	self.__valor = valor
	self.__estado = estado
	self.__aura = aura
	self.__propietario = propietario
	self.__contadores = contadores
	self.__etiquetas = etiquetas
	self.__sucesos = sucesos
	self.__max_cargas = max_cargas
	self.__fecha_fichero = fecha_fichero
	self.__contenedor  = contenedor 


    # Métodos normales

    def contener(self, contenedor):
	"""Pasa a estar contenido en el contenedor indicado.
	
	Requiere:
	    contenedor != None
	"""
	self.__contenedor = contenedor


    def liberar(self):
	"""Pasa a no estar contenido en ningún sitio (temporalmente, durante alguna operación)."""
	self.__contenedor = None


    def cambiar_cargas(self, cargas):
	"""Modifica el valor de la propiedad cargas.

	El valor del parámetro cargas es sumado al de la propiedad. Puede ser negativo. Si el
	resultado es menor que 0 o mayor que el máximo, se trunca.
	"""
	self.__cargas += cargas
	if self.__cargas < 0:
	    self.__cargas = 0
	if self.__cargas > self.__max_cargas:
	    self.__cargas = self.__max_cargas
	log.info("Objeto %s cambia %d cargas y queda con %d" %
		  (self.__id, cargas, self.__cargas))


    def cambiar_estado(self, puntos):
	"""Modifica el valor de la propiedad estado.

	El valor del parámetro puntos es sumado al de la propiedad. Puede ser negativo. Si el
	resultado es menor que 0 o mayor que 100, se trunca.
	"""
	self.__estado += estado
	if self.__estado < 0:
	    self.__estado = 0
	if self.__estado > 100:
	    self.__estado = 100
	log.info("Objeto %s cambia estado en %d puntos y queda con %d" %
		  (self.__id, puntos, self.__estado))


    def destruir(self):
	"""El objeto desaparece del juego."""
	if self.__contenedor != None:
	    self.__contenedor.objeto_desaparece(self)
	log.info("Objeto %s destruido" % self.__id)


    def reemplazar(self, id_objeto_nuevo):
	"""El objeto desaparece del juego, y otro objeto nuevo se coloca en su lugar."""
	if self.__contenedor != None:
	    objeto_nuevo = nuevo_xml(id_objeto_nuevo)
	    self.__contenedor.objeto_reemplazado(self, objeto_nuevo)
	    log.info("Objeto %s reemplazado por %s" % (self.__id, objeto_nuevo.id))


    def mover(self, sala):
	"""El objeto desaparece de donde esté, y aparece automáticamente en la sala destino."""
	if self.__contenedor != None:
	    self.__contenedor.objeto_desaparece(self)
	sala.objeto_aparece(self)
	log.info("Objeto %s se mueve a sala" % (self.__id, sala.id))


    def actuar(self, sala, texto):
	"""El objeto describe lo que le pasa y lo ven todos los que están en la sala."""
	log.info("Objeto %s %s" % (self.__id, texto))
	for personaje in sala.personajes.values():
	    if personaje.controlador != None:
		personaje.controlador.suceso_entorno(texto)


    def actuar_individual(self, personaje, texto):
	"""El objeto describe lo que le pasa y lo ve un personaje concreto."""
	log.info("Objeto %s a %s: %s" % (self.__id, personaje.id, texto))
	if personaje.controlador != None:
	    personaje.controlador.suceso_entorno(texto)


    def cambiar_propietario(self, nuevo_propietario):
	"""Cambia el propietario del objeto a un nuevo personaje.

	Si nuevo_propietario es None, el objeto pasa a ser sin dueño.
	"""
	if nuevo_propietario == None:
	    self.__propietario = None
	    log.info("Objeto %s pasa a no tener propietario" % self.__id)
	else:
	    self.__propietario = nuevo_propietario.id.split("%")[0]
	    log.info("Objeto %s pasa a ser propiedad de %s" % (self.__id,
		                                               nuevo_propietario.id))


    def personaje_mira(self, observador, sala):
	"""Responde al suceso de que un personaje mira la descripción de este objeto.

	Por cada item de descripcion, chequea los requisitos y realiza las acciones
	correspondientes. Al final devuelve una lista con todos los items de descripción
	cuyo módulo de respuesta ha devuelto un resultado de éxito.

	Requiere:
	    observador != None
	    sala != None
	"""
	items_devueltos = []

	log.info("Objeto %s es observado por %s" % (self.__id, observador.id))
	for item in self.__descripcion:
	    if item["personaje_mira"] == None:
		items_devueltos.append(item)
	    else:
		respuesta = item["personaje_mira"]
		result = respuesta.ejecutar(macros_valores = {"$observador": observador,
							      "$sala": sala,
							      "$objeto": self})

		if result == None:  # Si ninguna acción acepta o cancela explícitamente el
		    result = 1      # suceso, se asume que se acepta

		if result:
		    items_devueltos.append(item)

	return items_devueltos


    def personaje_come(self, usuario):
	return self.__responder_suceso_personaje("personaje_come", usuario)

    def personaje_bebe(self, usuario):
	return self.__responder_suceso_personaje("personaje_bebe", usuario)
 
    def personaje_lanza(self, usuario):
	return self.__responder_suceso_personaje("personaje_lanza", usuario)

    def personaje_usa(self, usuario):
	return self.__responder_suceso_personaje("personaje_usa", usuario)

    def personaje_pone_cabeza(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_cabeza", usuario)

    def personaje_pone_cuello(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_cuello", usuario)

    def personaje_pone_tronco(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_tronco", usuario)

    def personaje_pone_brazo_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_brazo_izq", usuario)

    def personaje_pone_brazo_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_brazo_der", usuario)

    def personaje_pone_mano_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_mano_izq", usuario)

    def personaje_pone_mano_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_mano_der", usuario)

    def personaje_pone_dos_manos(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_dos_manos", usuario)

    def personaje_pone_piernas(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_piernas", usuario)

    def personaje_pone_tobillo_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_tobillo_izq", usuario)

    def personaje_pone_tobillo_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_tobillo_der", usuario)

    def personaje_pone_pie_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_pie_izq", usuario)

    def personaje_pone_pie_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_pone_pie_der", usuario)
      
    def personaje_quita_cabeza(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_cabeza", usuario)

    def personaje_quita_cuello(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_cuello", usuario)

    def personaje_quita_tronco(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_tronco", usuario)

    def personaje_quita_brazo_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_brazo_izq", usuario)

    def personaje_quita_brazo_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_brazo_der", usuario)

    def personaje_quita_mano_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_mano_izq", usuario)

    def personaje_quita_mano_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_mano_der", usuario)

    def personaje_quita_dos_manos(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_dos_manos", usuario)

    def personaje_quita_piernas(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_piernas", usuario)

    def personaje_quita_tobillo_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_tobillo_izq", usuario)

    def personaje_quita_tobillo_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_tobillo_der", usuario)

    def personaje_quita_pie_izq(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_pie_izq", usuario)

    def personaje_quita_pie_der(self, usuario):
	return self.__responder_suceso_personaje("personaje_quita_pie_der", usuario)


    def __responder_suceso_personaje(self, suceso, usuario):
	"""Todas las respuestas a sucesos de personaje son iguales, asi que las pongo aquí.

	Chequea los requisitos y realiza las acciones correspondientes, y devuelve 1
	si se ha podido realizar el suceso con éxito, y 0 si no.

	Requiere:
	    usuario != None
	"""
	log.info("Objeto %s recibe suceso %s de %s" % (self.__id,
	                                               suceso,
						       usuario.id))
	if not self.__sucesos.has_key(suceso):
	    return 0
	else:
	    respuesta = self.__sucesos[suceso]
	    result =  respuesta.ejecutar(macros_valores = {"$usuario": usuario,
					   	           "$sala": usuario.sala,
						           "$objeto": self})

	    if result == None:  # Si ninguna acción acepta o cancela explícitamente el
		result = 1      # suceso, se asume que se acepta

	    return result


    def personaje_usa_con(self, usuario, objeto2):
	"""El suceso personaje_usa_con es casi igual, pero tiene un parámetro más.

	Chequea los requisitos y realiza las acciones correspondientes, y devuelve 1
	si se ha podido realizar el suceso con éxito, y 0 si no.

	Requiere:
	    usuario != None
	    objeto2 != None
	"""
	log.info("Objeto %s recibe suceso personaje_usa_con de %s con %s" %
		 (self.__id, usuario.id, objeto2.id))
	if not self.__sucesos.has_key("personaje_usa_con"):
	    return 0
	else:
	    respuesta = self.__sucesos["personaje_usa_con"]
	    result =  respuesta.ejecutar(macros_valores = {"$usuario": usuario,
					   	           "$sala": usuario.sala,
						           "$objeto": self,
						           "$objeto2": objeto2})

	    if result == None:  # Si ninguna acción acepta o cancela explícitamente el
		result = 1      # suceso, se asume que se acepta

	    return result


#    def puede_comer(self, usuario):
#	"""Comprueba si el usuario puede 'comer' este objeto.
#
#	Requiere:
#	    usuario != None
#	"""
#	encontrado = 0
#	for uso in self.usos:
#	    if uso["tipo"] == "comer":
#		encontrado = 1
#		for requisito in uso["requisitos"]:
#		    if not requisito.verificar({"$usuario": usuario,
#		                                "$sala": usuario.sala,
#						"$objeto": self}):
#		        return 0
#	return encontrado
#
#
#
#    def comer(self, usuario):
#	"""Realiza el uso 'comer' sobre este objeto.
#	
#	Ocurren todas las acciones asociados a este uso.
#
#	Requiere:
#	    usuario != None
#            puede_comer(usuario)
#	"""
#	for uso in self.usos:
#	    if uso["tipo"] == "comer":
#                for accion in uso["acciones"]:
#		    if accion.accion == "usar":
#			accion.ejecutar({"$usuario": usuario,
#			                "$sala": usuario.sala,
#					"$objeto": self})
#    
#
#    def puede_usar(self, usuario):
#	"""Comprueba si el usuario puede 'usar' este objeto.
#
#	Requiere:
#	    usuario != None
#	"""
#	encontrado = 0
#	for uso in self.usos:
#	    if uso["tipo"] == "usar":
#		encontrado = 1
##		for requisito in uso["requisitos"]:
##		    if not requisito.verificar({"$usuario": usuario,
##		                                "$sala": usuario.sala,
##						"$objeto": self}):
##		        return 0
#	return encontrado
#
#
#
#    def usar(self, usuario):
#	"""Realiza el uso 'usar' sobre este objeto.
#	
#	Ocurren todas las acciones asociados a este uso.
#
#	Requiere:
#	    usuario != None
#            puede_usar(usuario)
#	"""
#	for uso in self.usos:
#	    if uso["tipo"] == "usar":
#                for accion in uso["acciones"]:
#		    if accion.accion == "usar":
#			accion.ejecutar({"$usuario": usuario,
#			                "$sala": usuario.sala,
#					"$objeto": self})


    def contador(self, nombre_contador):
	"""Devuelve el valor del contador con el nombre indicado.

	Es útil para requisitos. Si no existe, devuelve None.

	Requiere:
	    nombre_contador != None
	"""
	try:
	    valor = self.__contadores[nombre_contador]
	    log.info("Objeto %s contador %s=%d" % (self.__id, nombre_contador, valor))
	except KeyError:
	    valor = None
	    log.warning("Objeto %s no tiene contador %s" % (self.__id, nombre_contador))
	return valor


    def cambiar_contador(self, nombre_contador, incr_valor):
	"""Suma la cantidad indicada al contador.

	Si el nombre del contador no existe, no hace nada.

	Requiere:
	    nombre_contador != None
	    incr_valor != None
	"""
	if self.__contadores.has_key(nombre_contador):
	    self.__contadores[nombre_contador] += incr_valor
	    log.info("Objeto %s cambia contador %s en %d y queda %d" %
		     (self.__id, nombre_contador, incr_valor,
		      self.__contadores[nombre_contador]))
	else:
	    log.warning("Objeto %s no tiene contador %s" % (self.__id, nombre_contador))


    def crear_contador(self, nombre_contador, valor):
	"""Crea un contador nuevo, con el valor indicado.

	Si ya existía, es sustituido.

	Requiere:
	    nombre_contador != None
	    valor != None
	"""
	self.__contadores[nombre_contador] = valor
	log.info("Objeto %s crea contador %s con valor %d" %
		 (self.__id, nombre_contador, valor))


    def borrar_contador(self, nombre_contador):
	"""Destruye un contador.

	Si no existía, no hace nada.

	Requiere:
	    nombre_contador != None
	"""
	try:
	    del self.__contadores[nombre_contador]
	    log.info("Objeto %s borra contador %s" % (self.__id, nombre_contador))
	except KeyError:
	    log.warning("Objeto %s no tiene contador %s" % (self.__id, nombre_contador))
	

    def etiqueta(self, nombre_etiqueta):
	"""Devuelve el valor de la etiqueta con el nombre indicado.

	Es útil para requisitos. Si no existe, devuelve None.

	Requiere:
	    nombre_etiqueta != None
	"""
	try:
	    valor = self.__etiquetas[nombre_etiqueta]
	    log.info("Objeto %s etiqueta %s=%s" % (self.__id, nombre_etiqueta, valor))
	except KeyError:
	    valor = None
	    log.warning("Objeto %s no tiene etiqueta %s" % (self.__id, nombre_etiqueta))
	return valor


    def cambiar_etiqueta(self, nombre_etiqueta, valor):
	"""Asigna el valor textual indicado a la etiqueta.

	Si el nombre de la etiqueta no existe, no hace nada.

	Requiere:
	    nombre_etiqueta != None
	    valor != None
	"""
	if self.__etiquetas.has_key(nombre_etiqueta):
	    self.__etiquetas[nombre_etiqueta] = valor
	    log.info("Objeto %s cambia etiqueta %s a %s" %
		     (self.__id, nombre_etiqueta, valor))
	else:
	    log.warning("Objeto %s no tiene etiqueta %s" % (self.__id, nombre_etiqueta))


    def crear_etiqueta(self, nombre_etiqueta, valor):
	"""Crea una etiqueta nueva, con el valor indicado.

	Si ya existía, es sustituida.

	Requiere:
	    nombre_etiqueta != None
	    valor != None
	"""
	self.__etiquetas[nombre_etiqueta] = valor
	log.info("Objeto %s crea etiqueta %s con valor %s" %
		 (self.__id, nombre_etiqueta, valor))


    def borrar_etiqueta(self, nombre_etiqueta):
	"""Destruye una etiqueta.

	Si no existía, no hace nada.

	Requiere:
	    nombre_etiqueta != None
	"""
	try:
	    del self.__etiquetas[nombre_etiqueta]
	    log.info("Objeto %s borra etiqueta %s" % (self.__id, nombre_etiqueta))
	except KeyError:
	    log.warning("Objeto %s no tiene etiqueta %s" % (self.__id, nombre_etiqueta))
	
	
    def activar(self):
	"""Realiza algunos chequeos periodicos de integridad del objeto.
	Los chequeos se pueden hacer, por ejemplo, al coger el objeto.
	"""
	archivo = os.path.join(DIR_DESC_OBJETOS, self.__id + EXT_DESC_OBJETOS)
	if self.__fecha_fichero < os.path.getmtime(archivo):
	    # Si se modifica el fichero descriptor, recargarlo de nuevo
	    # reseteando completamente el objeto
	    log.info("Recargando objeto " + self.__id + " desde el fichero de descripcion")
	    self.__recargar_objeto()

	if type(self.__peso) != type(1.0):
	    # En algunas versiones, por error, el atributo peso se estaba leyendo con tipo
	    # string en vez de float.
	    log.info("Arreglado peso de " + self.__id)
	    self.__peso = float(self.__peso)


    # Métodos especiales

    def __setstate__(self, atributos):
        """Método llamado para restaurar los atributos provinientes de un pickle."""

	self.__dict__ = atributos
 
        # Verificar la versión.
	if not self.__dict__.has_key("_Objeto__version"):
	    raise ValueError("Cargado objeto " + self.id + " obsoleto: sin versión," +
		             " cuando se esperaba " + VERSION_OBJETO)

        assert(VERSION_OBJETO == "1.0.6")
	while self.__version != "1.0.6":
 
	    # La versión 1.0.1 produce una ruptura con las anteriores ya que se migra al formato
	    # de clases de Python 2.2, y no es compatible con el anterior. Hay que borrar todas
	    # las salas, personajes y objetos.

            if self.__version == "1.0.1":
                # Versión 1.0.1 -> 1.0.2: añadir __max_cargas, borrar usos y crear sucesos.
		# Se crea sin sucesos, pero al activarlo se reseteará a partir del nuevo
		# fichero descriptor XML.
                self.__max_cargas = self.__cargas
		del self.__usos
		self.__sucesos = {}
                self.__version = "1.0.2"
                log.info("Convertido objeto " + self.__id + " a versión 1.0.2")
	    elif self.__version == "1.0.2":
		# Versión 1.0.2 -> 1.0.3: añadir __contenedor. En realidad en este momento
		# no podemos saber cual es el contenedor real, así que lo inicializamos a None.
		# Esto es una inconsistencia, pero se arregla en el momento en el que alguien
		# coja o deje el objeto. Si alguien lo usa antes de cogerlo o dejarlo, puede
		# que ocurra algo raro, pero esperemos que no sea muy grave.
		self.__contenedor = None
                self.__version = "1.0.3"
                log.info("Convertido objeto " + self.__id + " a versión 1.0.3")
	    elif self.__version == "1.0.3":
		# Versión 1.0.3 -> 1.0.4: añadir suceso personaje_mira a los items de descripción.
		for item in self.__descripcion:
		    item["personaje_mira"] = None
                self.__version = "1.0.4"
                log.info("Convertido objeto " + self.__id + " a versión 1.0.4")
	    elif self.__version == "1.0.4":
		# Versión 1.0.4 -> 1.0.5: añadir propietario
                self.__propietario = None
                self.__version = "1.0.5"
                log.info("Convertido objeto " + self.__id + " a versión 1.0.5")
	    elif self.__version == "1.0.5":
		# Versión 1.0.5 -> 1.0.6: añadir contadores y etiquetas
		self.__contadores = {}
		self.__etiquetas = {}
                self.__version = "1.0.6"
                log.info("Convertido objeto " + self.__id + " a versión 1.0.6")
	    else:
		raise ValueError("Cargado objeto " + self.__id + " con versión desconocida: " +
				 self.__version + " cuando se esperaba " + VERSION_OBJETO)


    def __recargar_objeto(self):
	"""Recarga todos los datos del fichero de descripción XML."""

	log.debug("Recargando objeto %s de fichero" % self.__id)

	(id, version, autor, comentario, area, nombre, descripcion, tipo,
	   categoria, peso, volumen, cargas, valor, estado, aura, propietario,
	   contadores, etiquetas, sucesos, max_cargas, fecha_fichero) = \
		   _parsear_fichero(id)

        self.__id = id
	self.__version = version
	self.__autor = autor
	self.__comentario = comentario
	self.__area = area
	self.__nombre = nombre
	self.__descripcion = descripcion
	self.__tipo = tipo
	self.__categoria = categoria
	self.__peso = peso
	self.__volumen = volumen
	self.__cargas = cargas
	self.__valor = valor
	self.__estado = estado
	self.__aura = aura
	self.__propietario = propietario
	self.__contadores = contadores
	self.__etiquetas = etiquetas
	self.__sucesos = sucesos
	self.__max_cargas = max_cargas
	self.__fecha_fichero = fecha_fichero
	

# Funciones públicas

def nuevo_xml(id):
    """Crea un objeto nuevo a partir de un fichero XML de descripcion de objeto.

    Requiere:
	id != None
	id corresponde con un fichero existente
    """
    log.debug("Creando objeto %s desde fichero" % id)

    (id, version, autor, comentario, area, nombre, descripcion, tipo,
	categoria, peso, volumen, cargas, valor, estado, aura, propietario,
	contadores, etiquetas, sucesos, max_cargas, fecha_fichero) = \
		_parsear_fichero(id)

    contenedor = None

    objeto = Objeto(id, version, autor, comentario, area, nombre, descripcion, tipo,
	            categoria, peso, volumen, cargas, valor, estado, aura, propietario,
		    contadores, etiquetas, sucesos, max_cargas, fecha_fichero, contenedor)

    return objeto


def _parsear_fichero(id):
    """Analiza un fichero XML y devuelve toda su información.

    La estructura del fichero XML es:

      <objeto version="xxx" autor="xxx" comentario="xxx" area="xxx">
        <id>xxx</id>
	<nombre>xxx</nombre>
	<descripcion>
	  <item>xxx
	    <NOMBRE_SUCESO>
	      ...guion...
	    </NOMBRE_SUCESO>
	    ...
	  </item>
	  ...
	</descripcion>
	<propiedades tipo="xxx" categoria="xxx", peso="xxx", volumen="xxx",
	             cargas="xxx", valor="xxx", estado="xxx", aura="xxx",
		     propietario="xxx">
	  <contador nombre="xxx" valor="xxx"/>
	  ...
	  <etiqueta nombre="xxx" valor="xxx"/>
	  ...
	</propiedades>
	<sucesos>
	  <NOMBRE_SUCESO>
	    ...guion...
	  </NOMBRE_SUCESO>
	  ...
	</sucesos>
      </objeto>

    Requiere:
      id != None
      id corresponde con un fichero existente
    """
    
    archivo = os.path.join(DIR_DESC_OBJETOS, id + EXT_DESC_OBJETOS)
    fecha_fichero = os.path.getmtime(archivo)

    elem_objeto = xmlutils.parsear_fichero(archivo)
    (version, autor, comentario, area) = _parsear_objeto(id, elem_objeto)

    elem_id = xmlutils.subelemento(elem_objeto, 'id')
    id_fichero = _parsear_id(id, elem_id)

    elem_nombre = xmlutils.subelemento(elem_objeto, 'nombre')
    nombre = _parsear_nombre(id, elem_nombre)
    
    elem_descripcion = xmlutils.subelemento(elem_objeto, 'descripcion')
    descripcion = _parsear_descripcion(id, elem_descripcion)

    elem_propiedades = xmlutils.subelemento(elem_objeto, 'propiedades')
    (tipo, categoria, peso, volumen, cargas, max_cargas, valor, estado, aura, propietario,
        contadores, etiquetas) =  _parsear_propiedades(id, elem_propiedades)

    elem_sucesos = xmlutils.subelemento(elem_objeto, 'sucesos')
    sucesos = _parsear_sucesos(id, elem_sucesos)
 
#	elem_usos = xmlutils.subelemento(elem_objeto, 'usos')
#	usos = _parsear_usos(elem_usos)

    return (id, version, autor, comentario, area, nombre, descripcion, tipo,
	    categoria, peso, volumen, cargas, valor, estado, aura, propietario,
	    contadores, etiquetas, sucesos, max_cargas, fecha_fichero)

 
def _parsear_objeto(id, elem_objeto):
    """Analiza el elemento <objeto>. Devuelve: 
	- version
	- autor
	- comentario
	- area
    """

    version_desc_objeto = xmlutils.atributo(elem_objeto, 'version')
    if version_desc_objeto != VERSION_DESC_OBJETO:
	raise ValueError("Version incorrecta del fichero descriptor de objeto " +
			 id + ": " + version_desc_objeto + " cuando se esperaba " +
			 VERSION_DESC_OBJETO)
			 
    version = VERSION_OBJETO
    autor = xmlutils.atributo(elem_objeto, 'autor')
    comentario = xmlutils.atributo_def(elem_objeto, 'comentario', '')
    area = xmlutils.atributo_def(elem_objeto, 'area', '')

    return (version, autor, comentario, area)

	
def _parsear_id(id, elem_id):
    """Analiza el elemento <id>. Devuelve:
        - id_fichero
    """
    id_fichero = xmlutils.contenido(elem_id)
    if id_fichero != id:
	raise ValueError("El id %s no coincide con el nombre del fichero %s" % (id, id_fichero))

    return id_fichero


def _parsear_nombre(id, elem_nombre):
    """Analiza el elemento <nombre>. Devuelve:
        - nombre
    """
    nombre = xmlutils.contenido(elem_nombre)
    return nombre


def _parsear_descripcion(id, elem_descripcion):
    """Analiza el elemento <descripcion> y todos sus subelementos. Devuelve:
        - descripcion
    """
    descripcion = []

    for elem_item in xmlutils.subelementos(elem_descripcion, 'item'):

	texto_item = xmlutils.contenido(elem_item)
	texto_item = " ".join(texto_item.split()) # elimina todos los espacios redundantes	    
	
	dif_item = xmlutils.atributo_int_def(elem_item, 'dificultad', 0,
		                             "Error en objeto %s. La dificultad de la descripción "
				             "tiene que ser un número." % id)
	if not dif_item in range(0, 101):
	    raise ValueError("Error en objeto %s. La dificultad de la descripción "
	                     "tiene que ser de 0 a 100." % id)

	modulo = None
	for elem_suceso in xmlutils.subelementos(elem_item):
	    suceso = xmlutils.nombre_elemento(elem_suceso)
	    if suceso == "personaje_mira":
		modulo = Modulo.nuevo_xml(elem_suceso,
					  macros_tipos = [ ("$observador", "Personaje"),
							   ("$sala", "Sala") ])

	descripcion.append({"dificultad": dif_item, 
		            "texto": texto_item,
			    "personaje_mira": modulo})

    return descripcion
		

def _parsear_propiedades(id, elem_propiedades):	
    """Analiza el elemento <propiedades> y todos sus subelementos. Devuelve:
        - tipo
	- categoria
	- peso
	- volumen
	- cargas
	- max_cargas
	- valor
	- estado
	- aura
	- propietario
	- contadores
	- etiquetas
    """
	    
    tipo = xmlutils.atributo_def(elem_propiedades, 'tipo', '')
    categoria = xmlutils.atributo_def(elem_propiedades, 'categoria', '')
    peso = xmlutils.atributo_float_def(elem_propiedades, 'peso', 0,
	       "Error en objeto %s. El peso tiene que ser un número." % id)
    volumen = xmlutils.atributo_float_def(elem_propiedades, 'volumen', 0,
	       "Error en objeto %s. El volumen tiene que ser un número." % id)
    cargas = xmlutils.atributo_int_def(elem_propiedades, 'cargas', 1,
	       "Error en objeto %s. Las cargas tienen que ser un número." % id)
    max_cargas = cargas
    valor = xmlutils.atributo_int_def(elem_propiedades, 'valor', 1,
	       "Error en objeto %s. El valor tiene que ser un número." % id)
    estado = xmlutils.atributo_int_def(elem_propiedades, 'estado', 0,
	       "Error en objeto %s. El estado tiene que ser un número." % id)
    aura = xmlutils.atributo_int_def(elem_propiedades, 'aura', 1,
	       "Error en objeto %s. El aura tiene que ser un número." % id)
    propietario = xmlutils.atributo_def(elem_propiedades, 'propietario', None)
    
    contadores = {}
    for elem_contador in xmlutils.subelementos(elem_propiedades, 'contador'):
	nombre_contador = xmlutils.atributo(elem_contador, 'nombre')
	valor_contador = xmlutils.atributo_float(elem_contador, 'valor', 
		      "Error en objeto %s. El valor del contador %s tiene que ser un número." %
		      (id, nombre_contador))
	contadores[nombre_contador] = valor_contador
    
    etiquetas = {}
    for elem_etiqueta in xmlutils.subelementos(elem_propiedades, 'etiqueta'):
	nombre_etiqueta = xmlutils.atributo(elem_etiqueta, 'nombre')
	valor_etiqueta = xmlutils.atributo(elem_etiqueta, 'valor')
	etiquetas[nombre_etiqueta] = valor_etiqueta

    return (tipo, categoria, peso, volumen, cargas, max_cargas, valor, estado, aura, propietario,
            contadores, etiquetas)


def _parsear_sucesos(id, elem_sucesos):
    """Analiza el elemento <sucesos> y todos sus subelementos. Devuelve:
        - sucesos
    """

    sucesos = {}

    for elem_suceso in xmlutils.subelementos(elem_sucesos):
	suceso = xmlutils.nombre_elemento(elem_suceso)
	if suceso in SUCESOS:
	    if suceso == "personaje_usa_con":
		respuesta = Modulo.nuevo_xml(elem_suceso,
					     macros_tipos = [ ("$usuario", "Personaje"),
							      ("$sala", "Sala"),
							      ("$objeto", "Objeto"),
							      ("$objeto2", "Objeto") ])
	    else:
		respuesta = Modulo.nuevo_xml(elem_suceso,
					     macros_tipos = [ ("$usuario", "Personaje"),
							      ("$sala", "Sala"),
							      ("$objeto", "Objeto") ])
		sucesos[suceso] = respuesta

    return sucesos
	    
       

    #    def _parsear_usos(self, elem_usos):
    #	"""Extrae toda la información de los usos del objeto."""
    #	
    #	limpiar_nodos_text_xml(elem_usos)
    #        self.__usos = []
    #
    #	for nodo_uso in elem_usos.childNodes[:]:
    #
    #	    uso_tipo = xmlutils.atributo(nodo_uso,'tipo')
    #            if nodo_uso.hasAttribute('posicion'):
    #		uso_posicion = xmlutils.atributo(nodo_uso,'posicion')
    #	    else:
    #	    	uso_posicion = ''
    ##            if nodo_uso.hasAttribute('gasto'):
    ##		uso_gasto = int(xmlutils.atributo(nodo_uso,'gasto'))
    ##	    else:
    ##		uso_gasto = 0
    ##	    if nodo_uso.hasAttribute('msg'):
    ##		uso_msg = xmlutils.atributo(nodo_uso,'msg')
    ##            else:
    ##		if uso_tipo == 'comer':
    ##		    uso_msg = 'Te comes ' + self.__nombre
    ##		elif uso_tipo == 'beber':
    ##		    uso_msg = 'Bebes ' + self.__nombre
    ##		elif uso_tipo == 'poner':
    ##		    uso_msg = 'Te pones ' + self.__nombre + ' en ' + uso_posicion
    ##		elif uso_tipo == 'lanzar':
    ##		    uso_msg = 'Arrojas ' + self.__nombre
    ##		else:
    ##		    uso_msg = 'Usas ' + self.__nombre
    ##            if nodo_uso.hasAttribute('duracion'):
    ##		uso_duracion = int(xmlutils.atributo(nodo_uso,'duracion'))
    ##	    else:
    ##		uso_duracion = 0
    #	
    #	    #Siguiente nivel del árbol
    #	    limpiar_nodos_text_xml(nodo_uso)
    #	    
    #	    uso_requisitos = []
    #	    uso_acciones = []
    #
    #	    for nodo_requi_suce in nodo_uso.childNodes[:]:
    #
    #		nombre_item = extraer_nombre_xml(nodo_requi_suce)
    #		if nombre_item == 'requisito':
    #	            requisito_atrib = xmlutils.atributo(nodo_requi_suce,'atributo')
    #	            requisito_valor = int(xmlutils.atributo(nodo_requi_suce,'valor'))
    #		    if nodo_requi_suce.hasAttribute('msg'):
    #                        requisito_msg = xmlutils.atributo(nodo_requi_suce,'msg')
    #                    else:
    #			requisito_msg = 'No tienes suficiente ' + requisito_atrib
    #		    uso_requisitos.append( {"atributo":requisito_atrib,
    #		                            "valor":requisito_valor,
    #					    "msg":requisito_msg} )
    #		else:
    #	            #accion_atrib = xmlutils.atributo(nodo_requi_suce,'atributo')
    #	            #accion_valor = int(xmlutils.atributo(nodo_requi_suce,'valor'))
    #                    #if nodo_requi_suce.hasAttribute('msg'):
    #	            #    accion_msg = xmlutils.atributo(nodo_requi_suce,'msg')
    #		    #else:
    #		    #    accion_msg = ""
    #		    #uso_acciones.append( {"atributo":accion_atrib,
    #		    #                     "valor":accion_valor,
    #	 	    #                     "msg":accion_msg} )
    #		    uso_acciones.append(Accion(elem_accion = nodo_requi_suce,
    #		                              acciones = [ "usar", "descubrir" ],
    #					      macros_tipos = [ ("$usuario", "Personaje"),
    #					                       ("$sala", "Sala"),
    #							       ("$objeto", "Objeto") ]))
    #
    #	    self.__usos.append( {"tipo":uso_tipo,
    #				 "posicion":uso_posicion,
    ##				 "gasto":uso_gasto,
    ##				 "msg":uso_msg,
    #				 "duracion":uso_duracion,
    #				 "requisitos":uso_requisitos,
    #				 "acciones":uso_acciones} )

# Código para pruebas unitarias del módulo.

if (__name__ == '__main__'):
    objeto = nuevo_xml("brandy01")
    print "id = " + objeto.id
    print "version = " + objeto.version
    print "autor = " + objeto.autor
    print "comentario = " + objeto.comentario
    print "area = " + objeto.area
    print "nombre = " + objeto.nombre

    print "descripcion = ["
    for item in objeto.descripcion:
	print "  " + str(item["dificultad"]) + ": '" + item["texto"] + "'"
    print "]"

    print "tipo = " + objeto.tipo
    print "categoria = " + objeto.categoria
    print "peso = " + str(objeto.peso)
    print "volumen = " + str(objeto.volumen)
    print "cargas = " + str(objeto.cargas)
    print "valor = " + str(objeto.valor)
    print "estado = " + str(objeto.estado)
    print "aura = " + str(objeto.aura)

    def imprimir_secuencia(secuencia, indentacion=""):
	for respuesta in secuencia:
	    if respuesta.__class__.__name__ == "Accion":
		print indentacion + "    * accion base = " + respuesta.base
		print indentacion + "             nombre = " + respuesta.nombre
		print indentacion + "             parametros = [",
		primer2 = 1
		for parametro in respuesta.parametros:
		    if primer2: primer2 = 0
		    else: print ", ",
		    print parametro["nombre"] + "(" + parametro["tipo"] + ") = '" + parametro["valor"] + "'",
		print "]"
	    elif respuesta.__class__.__name__ == "Requisito":
		print indentacion + "    * requisito base = " + respuesta.base
		print indentacion + "                nombre = " + respuesta.nombre
		print indentacion + "                parametros = [",
		primer2 = 1
		for parametro in respuesta.parametros:
		    if primer2: primer2 = 0
		    else: print ", ",
		    print parametro["nombre"] + "(" + parametro["tipo"] + ") = '" + parametro["valor"] + "'",
		print "]"
		print indentacion + "      no_cumplido:"
		imprimir_secuencia(respuesta.no_cumplido.secuencia, indentacion + "   ")
		print indentacion + "      cumplido:"
		imprimir_secuencia(respuesta.cumplido.secuencia, indentacion + "   ")
	    elif respuesta.__class__.__name__ == "Modulo":
		print indentacion + "    * módulo id = " + respuesta.id
		print indentacion + "             version = " + respuesta.version
		print indentacion + "             autor = " + respuesta.autor
		print indentacion + "             comentario = " + respuesta.comentario
		print indentacion + "             macros_extra = " + str(respuesta._Modulo__macros_extra)
		print indentacion + "      secuencia:"
		imprimir_secuencia(respuesta.secuencia, indentacion + "   ")

    print "sucesos = ["
    primer = 1
    for suceso in objeto.sucesos.keys():
	if primer: primer = 0
	else: print ""

	print " -> suceso = " + suceso
	imprimir_secuencia(objeto.sucesos[suceso].secuencia)
    print "]" 

#    print "usos = ["
#    primer = 1
#    for uso in objeto.usos:
#	if primer: primer = 0
#	else: print ""
#	print " -> tipo = " + uso["tipo"]
#	print "    posicion = " + uso["posicion"]
#	for accion in uso["acciones"]:
#	    print "    accion tipo = " + accion.tipo
#	    print "           accion = " + accion.accion
#	    print "           parametros = [",
#	    primer2 = 1
#	    for parametro in accion.parametros:
#		if primer2: primer2 = 0
#		else: print ", ",
#		print parametro["nombre"] + "(" + parametro["tipo"] + ") = " + parametro["valor"],
#	    print "]"
#    print "]" 

