# Copyright 2016 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
#
# This module provides an extended subset of the asyncio.futures.Futures
# interface.

__all__ = (
    "CancelledError",
    "ExtendedFuture",
    "InvalidStateError",
)

import concurrent.futures
from concurrent.futures import CancelledError, Future

try:
    from concurrent.futures import InvalidStateError
except ImportError:
    from portage.util.futures.futures import InvalidStateError

# Create our one time settable unset constant
UNSET_CONST = Future()
UNSET_CONST.set_result(object())


class ExtendedFuture(Future):
    """Extended Future class adding convienince get and set operations with
    default result capabilities for unset result().  It also adds pass
    capability for duplicate set_result() calls.
    """

    def __init__(self, default_result=UNSET_CONST.result()):
        """Class init

        @param default_result: Optional data type/value to return in the event
                               of a result() call when result has not yet been
                               set.
        """
        self.default_result = default_result
        super().__init__()
        self.set = self.set_result

    def set_result(self, data, ignore_InvalidState=False):
        """Set the Future's result to the data, optionally don't raise
        an error for 'InvalidStateError' errors

        @param ignore_exception: Boolean
        """
        if ignore_InvalidState:
            try:
                super().set_result(data)
            except InvalidStateError:
                pass
        else:
            super().set_result(data)

    def get(self, default=UNSET_CONST.result()):
        """Convenience function to wrap result() but adds an optional
        default value to return rather than raise an InvalidStateError

        @param default: Optional override for the classwide default_result
        @returns: the result data or the default value, raisies an exception
                  if result is unset and no default is defined.
        """
        if default is not UNSET_CONST.result():
            pass
        elif self.default_result is not UNSET_CONST.result():
            default = self.default_result
        if default is not UNSET_CONST.result():
            try:
                data = self.result()
            except InvalidStateError:
                data = default
        else:
            data = self.result()
        return data

    def exception(self):
        try:
            return super().exception(timeout=0)
        except concurrent.futures.TimeoutError:
            raise InvalidStateError

    def result(self):
        try:
            return super().result(timeout=0)
        except concurrent.futures.TimeoutError:
            raise InvalidStateError
