import random
import logging
import datetime

from tornado.ioloop import IOLoop
from tornado import gen, locks

import random

from ..entity import entity_method, facet_create_handler
from .. import events, actions

SPAWN_STOP_TIMEOUT = 600

log = logging.getLogger(__name__)

@facet_create_handler('spawn')
def create_spawn(state):
    state.setdefault('max_count', 1)
    state.setdefault('distance', 0)
    state.setdefault('tags', [])
    state['is_active'] = None
    state['children'] = set()
    return state

@gen.coroutine
@entity_method
def update_spawn(self):
    log.debug('running update_spawn for %r', self.path)
    while True:
        try:
            min_delay, max_delay = self.spawn.delay
            delay = min_delay + random.random() * (max_delay - min_delay)
            yield self.spawn._stop_event.wait(datetime.timedelta(seconds=delay))
            return
        except gen.TimeoutError:
            pass

        # FIXME: weights
        if len(self.spawn.children) < self.spawn.max_count:
            path = random.choice(list(self.spawn.what.keys()))
            dest = random.choice(self.spawn.rooms)
            log.debug('spawning %s in %s', path, dest.path)
            obj = self.db.clone_path(path)
            obj._spawn = self.spawn
            self.spawn.children.add(obj)
            obj.teleport(dest.path)

@entity_method
def activate_spawn(self):
    spawn = getattr(self, 'spawn', None)
    if spawn is not None:
        if spawn.is_active is None:
            # This is the first time the spawn has been activated. Initialize it.
            spawn.rooms = [room for room, _ in self.get_nearby_locations(spawn.distance)
                           if not spawn.tags or any(room.has_tag(t) for t in spawn.tags)]
            spawn.is_active = False
            spawn._stop_event = locks.Event()
        if not spawn.is_active:
            spawn.is_active = True
            IOLoop.current().spawn_callback(self.update_spawn)

@events.subscriber
def did_enter(event):
    """
    Wakes up spawns in nearby rooms when a player enters a room.
    """
    for room in event.actor.nearby_rooms():
        room.activate_spawn()

def remove_spawn_child(event):
    spawn = getattr(event.target, '_spawn', None)
    if spawn is not None:
        spawn.children.discard(event.target)
        del event.target._spawn

events.subscribe(remove_spawn_child, 'did_take')
events.subscribe(remove_spawn_child, 'did_kill')
