# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Feedback query delegate interfaces and implementation registry."""

import multiprocessing

import common
from autotest_lib.client.common_lib.feedback import client


# Mapping of query identifiers to delegate classes.
_query_delegate_registry = {}


class _QueryDelegate(object):
    """A base class for query delegates."""

    _query_count = multiprocessing.Value('d', 0)

    def __init__(self, test, dut, multiplexer, atomic=True):
        """Constructs the delegate.

        @param test: The name of the test.
        @param dut: The name of the DUT.
        @param multiplexer: Feedback request multiplexer object.
        @param atomic: Whether this is an atomic query.
        """
        super(_QueryDelegate, self).__init__()
        self.test = test
        self.dut = dut
        self._multiplexer = multiplexer
        self._atomic = atomic

        # Assign a unique query number.
        with self._query_count.get_lock():
            self._query_num = self._query_count.value
            self._query_count.value += 1


    def _process_request(self, req):
        """Submits a given request to the multiplexer for processing."""
        return self._multiplexer.process_request(req, self._query_num,
                                                 self._atomic)


    def prepare(self, **kwargs):
        """Delegate for a query's prepare() method."""
        return self._prepare_impl(**kwargs)


    def _prepare_impl(self, **kwargs):
        """Concrete implementation of the query's prepare() call."""
        raise NotImplementedError


    def validate(self, **kwargs):
        """Delegate for a query's validate() method.

        This clears the atomic sequence with the multiplexer to make sure it
        isn't blocked waiting for more requests from this query.
        """
        try:
            return self._validate_impl(**kwargs)
        finally:
            if self._atomic:
                self._multiplexer.end_atomic_seq(self._query_num)


    def _validate_impl(self, **kwargs):
        """Concrete implementation of the query's validate() call."""
        raise NotImplementedError


class OutputQueryDelegate(_QueryDelegate):
    """A base class for output query delegates."""


class InputQueryDelegate(_QueryDelegate):
    """A base class for input query delegates."""

    def emit(self):
        """Delegate for an input query's emit() method."""
        return self._emit_impl()


    def _emit_impl(self):
        """Concrete implementation of the query's emit() call."""
        raise NotImplementedError


def register_delegate_cls(query_id, delegate_cls):
    """Registers a delegate class with a given query identifier.

    @param query_id: Query identifier constant.
    @param delegate_cls: The class implementing a delegate for this query.
    """
    _query_delegate_registry[query_id] = delegate_cls


def get_delegate_cls(query_id):
    """Returns a query delegate class for a given query type.

    @param query_id: A query type identifier.

    @return A query delegate class.

    @raise ValueError: Unknown query type.
    @raise NotImplementedError: Query type not supported.
    """
    if query_id not in client.ALL_QUERIES:
        raise ValueError
    if query_id not in _query_delegate_registry:
        raise NotImplementedError
    return _query_delegate_registry[query_id]