# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Author: Olivier Tilloy <olivier@fluendo.com>

from elisa.plugins.pigment.pigment_controller import PigmentController

from twisted.internet import task


class HierarchyController(PigmentController):

    """
    Generic base controller that provides some asynchronous loading facilities.

    Every deferred call done in the context of this controller should be
    registered in the internal dictionary of deferreds so as to keep track of
    them and allow their cancellation if needed (e.g. when cleaning the
    controller). Each value of this internal dictionary is a list, meaning that
    several deferred calls can be associated to one key.

        >>> self.register_deferred(key, deferred)

    To cancel all the deferred calls associated to a given key, do:

        >>> self.cancel_deferreds(key)

    @ivar deferreds: currently pending deferred calls
    @type deferreds: C{dict} of any immutable type -> C{list} of
                     {elisa.core.utils.defer.Deferred}
    """

    def __init__(self):
        super(HierarchyController, self).__init__()
        self.deferreds = {}

    def register_deferred(self, key, deferred):
        """
        Register a deferred call to be associated to a given key.
        """
        self.deferreds.setdefault(key, []).append(deferred)

    def cancel_deferreds(self, key):
        """
        Cancel all the currently pending deferred calls associated to one given
        key in the internal dictionary.

        @param key: the key in the internal deferreds dictionary
        @type key:  any immutable type
        """
        if not key in self.deferreds:
            return

        for deferred in self.deferreds[key]:
            if not deferred.called:
                deferred.cancel()

        del self.deferreds[key]

    def cancel_all_deferreds(self):
        """
        Cancel all the currently pending deferred calls.
        """
        def cancel_all():
            for key in self.deferreds.keys():
                self.cancel_deferreds(key)
                yield None

        return task.coiterate(cancel_all())

    def clean(self):
        dfr = self.cancel_all_deferreds()

        def parent_clean(result):
            return super(HierarchyController, self).clean()

        dfr.addCallback(parent_clean)
        return dfr
