import argparse
import redis

from ._argparse import get_password


class Container(object):
    """
    Ancestor for containers.
    """
    pass


class ParameterContainer(Container):
    """
    Simple container to store parameters.
    """

    def __init__(self):
        self.redis = None
        """ The redis connection. """

        self.pubsub = None
        """ The pub/sub instance. """

        self.channel_in = None
        """ The channel to receive the data from. """

        self.channel_out = None
        """ The channel to send the processed data to. """

        self.timeout = 0.001
        """ The timeout to use for the pubsub thread sleep_time parameter. """

        self.stopped = False
        """ Flag whether processing has been stopped. """


class MessageContainer(Container):
    """
    Simple container to store message alongside parameter for handler.
    """

    def __init__(self):
        self.message = None
        """ The message received via redis. """

        self.params = None
        """ The parameter container to pass along. """


def configure_redis(ns, config=None):
    """
    Uses the namespace generated by argparse to configure redis and returns a configured ParameterContainer.

    :param ns: the parsed argparse namespace
    :type ns: argparse.Namespace
    :param config: additional parameters to make available via the "config" attribute in the returned container, eg a subclass of Container
    :return: the parameter container with the redis parts filled in
    :rtype: ParameterContainer
    """
    result = ParameterContainer()
    result.redis = redis.Redis(host=ns.redis_host, port=ns.redis_port, db=ns.redis_db, password=get_password(ns))
    result.pubsub = result.redis.pubsub()
    result.channel_in = ns.redis_in
    result.channel_out = ns.redis_out
    result.timeout = ns.redis_timeout
    if config is not None:
        result.config = config
    return result


def run_harness(params, process_method):
    """
    Starts the processing using the supplied parameters and method for processing MessageContainer objects.

    :param params: the parameters to use
    :type params: ParameterContainer
    :param process_method: the method to use for processing the MessageContainer
    """

    msg_cont = MessageContainer()
    msg_cont.params = params

    def anon_handler(message):
        try:
            msg_cont.message = message
            process_method(msg_cont)
        except KeyboardInterrupt:
            msg_cont.params.stopped = True

    params.pubsub.psubscribe(**{params.channel_in: anon_handler})
    params.pubsub.run_in_thread(sleep_time=params.timeout)
