#!/usr/bin/env python
#
#Ambar. Another MiddleEarth Based RPG
#Copyright (C) 2001  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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
#Fichero: salatester.py

"""Clase Sala."""


from xml.dom import minidom
from test_de_objeto import *
import random
import sys
import string
import os



class Sala:
    """Una habitacion o sala dentro del juego.
    
    Contiene los siguientes atributos:
     - id (string): identifica univocamente a la sala. No puede contener espacios.
     - version (string): versión del sistema de salas.
     - autor (string): nombre del autor de la descripción.
     - area (string): nombre del area a la que pertenece la sala.
     - nombre (string): texto con el nombre corto de la sala, tal como sale en pantalla.
     - aura (int): nivel de aura de la sala (50 = normal, 0-50 = malvado, 50-100 = bueno).
     - volumen (int): volumen total que puede contener (1 unidad = tamaño de un humano).
     - luz (int): nivel de iluminación (0 = oscuridad total, 100 = enfrente de Eru).
     - combate (string): si se puede combatir en esta sala ("si" o "no").
     - ocultabilidad (int): facilidad de ocultarse (0 = imposible, 100 = seguro).
     - descripcion ({String:any}[]): descripcion de la sala. Consiste en una lista de items,
                                     y cada item es un conjunto de propiedades en forma de
				     diccionario:
				      - "dificultad" (int): dificultad para percibir el item
				                            (0 = automatico, 100 = imposible)
				      - "texto" (string): el texto del item
     - salidas ({String:any}[]): lista de salidas de la sala, siendo cada salida un diccionario:
	                         - "direccion" (string): el punto cardinal (norte, sur, etc.)
				 - "dificultad" (int): dificultad para percibirla (0-100)
				 - "id" (string): el id de la sala a donde se va por ahi
				 - "descripcion" (string): lo que se ve al mirar en esa direccion
     - objetos ({String:any}[]): lista de objetos de la sala, siendo cada objeto un diccionario:
	                         - "id" (string): el id del objeto
				 - "cantidad" (int): cuantos objetos hay actualmente (si es 0 no
				                     hay ninguno, pero puede aparecer alguno
						     más tarde)
				 - "probabilidad" (int): probabilidad de que reaparezca cada vez
				                         que alguien entra en la sala (0-100)
				 - "maximo" (int): maximos objetos que pueden aparecer (si es
				                   0, no hay máximo)
				 - "dificultad" (int): dificultad para percibirlo (0-100)
				 - "descripcion" (string): que aspecto tiene el objeto
     - encuentros ({String:any}[]): lista de pnjs propios de la sala, siendo cada encuentro
                                    un diccionario:
	                            - "id" (string): el id del pnj
				    - "cantidad" (int): cuantos personajes hay actualmente (si
				                        es 0 no hay ninguno, pero puede aparecer
							alguno más tarde)
				    - "probabilidad" (int): probabilidad de que reaparezca cada
				                            vez que alguien entra en la sala
							    (0-100)
				    - "maximo" (int): maximos pnjs que pueden aparecer (si es
				                      0, no hay máximo)
				    - "dificultad" (int): dificultad para percibirlo (0-100)
				    - "descripcion" (string): que aspecto tiene el personaje
     - personajes (Personaje{}): diccionario con los personajes que se encuentran en la sala,
                              indexado por nombre. NO MODIFICAR DIRECTAMENTE, SINO CON
                              entrar_personaje() Y salir_personaje()
     - n_jugadores (int): numero de personajes de tipo PersonajeJugador dentro de la sala
    
    Al instanciar una sala, se crea nueva y se lee desde el fichero de descripcion de sala,
    poniendola en su estado inicial. Mas tarde, se puede salvar en un fichero de sala,
    borrarla y cargarla despues, manteniendo su estado (situacion de objetos y personajes
    contenidos, tanto PJs como PNJs).
    
    Importante: al salvar una sala se salvan también todos los objetos y todos los personajes
    contenidas en ella. El módulo "Pickle" se encarga de todo, incluyendo la resolución del bucle
    formado por el atributo "sala" de la clase Personaje.
    """

    DIR_DESC_SALAS = 'desc_salas'
    EXT_DESC_SALAS = '.xml'
    DIR_SALAS = 'salas'
    EXT_SALAS = ''
		   

    def __init__(self, id):
	
	"""Método inicial para leer los datos de un fichero de sala"""
	
	#print "TRAZA-> Sala.__init__: creando sala nueva desde fichero " + id + Sala.EXT_DESC_SALAS + "..."

	self.id = id
	
	archivo = self.id+Sala.EXT_DESC_SALAS
	#stream = openAnything(archivo)  Andres-> esta funcion tiene la pega de que si el 
	stream = open(archivo)         # archivo no existe da un error muy raro, y tampoco es
	doc_xml = minidom.parse(stream).documentElement         # necesaria en realidad ...
	stream.close()

	self.iniciar_parseador(doc_xml)
	self.parsear_sala(doc_xml)
	self.parsear_id(doc_xml)	
	self.parsear_nombre(doc_xml)	
	self.parsear_descripcion(doc_xml)
	self.parsear_salidas(doc_xml)
	self.parsear_propiedades(doc_xml)
	self.parsear_objetos(doc_xml)
	self.parsear_encuentros(doc_xml)

	self.personajes = {}
	self.n_jugadores = 0	
	
	#print "TRAZA-> Sala.__init__: terminado!"
    

    def iniciar_parseador(self, doc_xml):
	"""Esta función borra todos los nodos de tipo Text de un
	elemento DOM"""
	
	for nodo in doc_xml.childNodes[:]:
	    if not 'Element:' in str(nodo).split():
		doc_xml.childNodes.remove(nodo)

        
    def parsear_sala(self, doc_xml):
	"""Extrae la información del elemento sala:
	    area: el área a la que pertenece la sala
	    autor: el autor de la sala
	    version: la versión del tipo de sala"""
	self.version = doc_xml.attributes["version"].value.encode('ISO-8859-1')
	self.autor = doc_xml.attributes["autor"].value.encode('ISO-8859-1')
	self.area = doc_xml.attributes["area"].value.encode('ISO-8859-1')
	try:
	    self.comentario = doc_xml.attributes["comentario"].value.encode('ISO-8859-1')
	except:
	    self.comentario = "nada"
	
    def parsear_id(self, doc_xml):
	"""Extrae el id de la sala"""
	id_fichero = doc_xml.childNodes[0].childNodes[0].data.encode('ISO-8859-1')
	if id_fichero != self.id:
	    raise ValueError("El id no coincide con el nombre del fichero")
	

    def parsear_nombre(self, doc_xml):
	"""Extrae el nombre de la sala"""
	self.nombre = doc_xml.childNodes[1].childNodes[0].data.encode('ISO-8859-1')


    def parsear_propiedades(self, doc_xml):	
	"""Extrae las propiedades globales de la sala."""
	self.aura = int(doc_xml.childNodes[4].attributes["aura"].value)
	self.volumen = int(doc_xml.childNodes[4].attributes["volumen"].value)
	self.luz = int(doc_xml.childNodes[4].attributes["luz"].value)
	self.combate = doc_xml.childNodes[4].attributes["combate"].value.encode('ISO-8859-1')
	self.ocultabilidad = int(doc_xml.childNodes[4].attributes["ocultabilidad"].value)
	

    def parsear_descripcion(self, doc_xml):
	"""Extrae el texto y la dificultad asociada de los items de descripcion de la sala"""
	
	nodo_descripcion = doc_xml.childNodes[2]

	# Limpiamos todos los subnodos de tipo Text, al igual
	# que hacíamos en la función iniciar_parseador()
	for nodo in nodo_descripcion.childNodes[:]:
	    if not 'Element:' in str(nodo).split(): 
	        doc_xml.childNodes[2].childNodes.remove(nodo)
	
	self.descripcion = []

	for nodo_item in nodo_descripcion.childNodes[:]:

	    texto_item = ""
	    for nodo_parrafo in nodo_item.childNodes[:]:
		texto_item += nodo_parrafo.data.encode('ISO-8859-1')
	    
	    if len(nodo_item._attrs) == 0:
		dif_item = 0
	    else:
		dif_item = int(nodo_item._attrs.get('dificultad').value)
		
	    self.descripcion.append({"dificultad": dif_item, "texto": texto_item})

   
    def parsear_salidas(self, doc_xml):	
	"""Extrae toda la información de las salidas de la sala."""
	
	nodo_salidas = doc_xml.childNodes[3]
	
	# Limpiamos todos los subnodos de tipo Text, al igual
	# que hacíamos en la función iniciar_parseador()
	for nodo in nodo_salidas.childNodes[:]:
	    if not 'Element:' in str(nodo).split():
		nodo_salidas.childNodes.remove(nodo)

	self.salidas = []

	for nodo_salida in nodo_salidas.childNodes[:]:
	    
	    direccion_salida = nodo_salida.nodeName.encode('ISO-8859-1')  # oeste, este, norte, etc.
	    try:
		dif_salida = int(nodo_salida._attrs.get('dificultad').value.encode('ISO-8859-1'))
	    except:
		dif_salida = 0
	    id_salida = nodo_salida._attrs.get('id').value.encode('ISO-8859-1')
	    desc_salida = nodo_salida.childNodes[0].data.encode('ISO-8859-1')

	    self.salidas.append({"direccion": direccion_salida,
	                         "dificultad": dif_salida,
				 "id": id_salida, 
				 "descripcion": desc_salida})


    def parsear_objetos(self, doc_xml):
	"""Extrae toda la información de los objetos que hay en la sala inicialmente."""

	nodo_objetos = doc_xml.childNodes[5]

	# Limpiamos todos los subnodos de tipo Text, al igual
	# que hacíamos en la función iniciar_parseador()
	for nodo in nodo_objetos.childNodes[:]:
	    if  'Element:' not in str(nodo).split():
		nodo_objetos.childNodes.remove(nodo)

	self.objetos = []

	for nodo_objeto in nodo_objetos.childNodes[:]:

	    id_objeto = nodo_objeto._attrs.get('id').value.encode('ISO-8859-1')
	    try:
		cantidad_objeto = int(nodo_objeto._attrs.get('cantidad').value)
	    except: 
		cantidad_objeto = 1
	    try:
		max_objeto = int(nodo_objeto._attrs.get('maximo').value)
	    except: 
		max_objeto = 0
	    try:
		prob_objeto = int(nodo_objeto._attrs.get('probabilidad').value)
	    except: 
		prob_objeto = 0
	    try:
		dif_objeto = int(nodo_objeto._attrs.get('dificultad').value)
	    except:
		dif_objeto = 0
	    desc_objeto = nodo_objeto.childNodes[0].data.encode('ISO-8859-1')

            obj = Objeto(id_objeto)
	    
	    self.objetos.append({"id": id_objeto,
				 "cantidad": cantidad_objeto,
				 "maximo": max_objeto,
				 "probabilidad": prob_objeto,
				 "dificultad": dif_objeto,
				 "descripcion": desc_objeto,
                                 "instancia":obj})


    def parsear_encuentros(self, doc_xml):
	"""Extrae toda la información de los PNJs que hay en la sala inicialmente."""
	
	nodo_encuentros = doc_xml.childNodes[6]

	# Limpiamos todos los subnodos de tipo Text, al igual
	# que hacíamos en la función iniciar_parseador()
	for nodo in doc_xml.childNodes[6].childNodes[:]:
	    if 'Element:' not in str(nodo).split(): doc_xml.childNodes[6].childNodes.remove(nodo)

	self.encuentros=[]

	for nodo_encuentro in nodo_encuentros.childNodes[:]:
	    id_encuentro = nodo_encuentro._attrs.get('id').value.encode('ISO-8859-1')
	    try:
		cant_encuentro = int(nodo_encuentro._attrs.get('cantidad').value)
	    except: 
		cant_encuentro = 1
	    try:
		max_encuentro = int(nodo_encuentro._attrs.get('maximo').value)
	    except: 
		max_encuentro = 0
	    try:
		prob_encuentro = int(nodo_encuentro._attrs.get('probabilidad'))
	    except: 
		prob_encuentro = 0
	    try:
		dif_encuentro = int(nodo_encuentro._attrs.get('dificultad').value)
	    except: 
		dif_encuentro = 0
	    desc_encuentro = nodo_encuentro.childNodes[0].data.encode('ISO-8859-1')
	    
	    self.encuentros.append({"id": id_encuentro,
				    "cantidad": cant_encuentro,
				    "maximo": max_encuentro,
				    "probabilidad": prob_encuentro,
				    "dificultad": dif_encuentro,
				    "descripcion": desc_encuentro})

	
    def entrar_personaje(self, personaje):
        """Método llamado cuando un personaje entra en la sala."""
	
	#print "TRAZA-> Sala[" + self.id + "].entrar_personaje(" + personaje.id + ")"
        
        # Actualizar la lista
        self.personajes[personaje.id] = personaje
        
        # Actualizar el contador de PJs
        if isinstance(personaje, PersonajeJugador):
            self.n_jugadores += 1

	
            
    def salir_personaje(self, personaje):
        """Método llamado cuando un personaje sale de la sala."""
	
	#print "TRAZA-> Sala[" + self.id + "].salir_personaje(" + personaje.id + ")"
        
        # Actualizar la lista
        del self.personajes[personaje.id]
        
        # Actualizar el contador de PJs
        if isinstance(personaje, PersonajeJugador):
            self.n_jugadores -= 1


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

if (__name__ == '__main__'):
    try:
	id = sys.argv[1]
    except:
	id = raw_input('¿Qué id del directorio de desc_salas deseas probar? ')
    
    sala = Sala(id)
    print '\n************ SALA.PY **********\n'
    print 'Cargada sala con id:', sala.id, 'con nombre:', sala.nombre, "y comentario:", sala.comentario
    
    print 'Version:', sala.version, 'Autor:', sala.autor, 'Area:', sala.area, '\n'
    
    print 'DESCRIPCIONES:'
    for item in sala.descripcion:
	print 'dificultad %d: %s\n' % (item["dificultad"], item["texto"])
	
    print 'SALIDAS (salida: dificultad, id, descripcion):'
    for salida in sala.salidas:
	print '%s: %s, %s, %s' % (salida["direccion"], salida["dificultad"],
	salida["id"], salida["descripcion"])

    print '\nPROPIEDADES:'
    print 'Volumen:', sala.volumen, 'Luz:', sala.luz, 'Aura:', sala.aura, \
    'Combate:', sala.combate, 'Ocultabilidad:', sala.ocultabilidad, '\n'
    
    print 'OBJETOS (id: cantidad, maximo, probabilidad, dificultad, descripcion)'
    for objeto in sala.objetos:
	print '%s: %d, %d, %d, %d, %s' % (objeto["id"], objeto["cantidad"], objeto["maximo"],
                                              objeto["probabilidad"], objeto["dificultad"], 
                                              objeto["descripcion"])
        print objeto["instancia"]
        
    print '\nENCUENTROS (id : cantidad, maximo, probabilidad, dificultad, descripcion)'
    for encuentro in sala.encuentros:
        print '%s: %d, %d, %d, %d, %s' % (encuentro["id"], encuentro["cantidad"],
                                          encuentro["maximo"], encuentro["probabilidad"],
                                          encuentro["dificultad"], encuentro["descripcion"])

