import numpy as np

from elsim.methods._common import (_all_indices, _get_tiebreak, _no_tiebreak,
                                   _order_tiebreak_keep, _random_tiebreak)

_tiebreak_map = {'order': _order_tiebreak_keep,
                 'random': _random_tiebreak,
                 None: _no_tiebreak}


def utility_winner(utilities, tiebreaker=None):
    """
    Find the utilitarian winner of an election. (Dummy "election method").

    Given a set of utilities for each voter-candidate pair, find the candidate
    who maximizes summed utility across all voters. [1]_

    Parameters
    ----------
    utilities : array_like
        A 2D collection of utilities.

        Rows represent voters, and columns represent candidate IDs.
        Higher utility numbers mean greater approval of that candidate by that
        voter.

    tiebreaker : {'random', 'order', None}, optional
        If there is a tie, and `tiebreaker` is ``'random'``, a random finalist
        is returned.
        If 'order', the lowest-ID tied candidate is returned.
        By default, ``None`` is returned for ties.

    Returns
    -------
    winner : int
        The ID number of the winner, or ``None`` for an unbroken tie.

    References
    ----------
    .. [1] S. Merrill III, "A Comparison of Efficiency of Multicandidate
       Electoral Systems", American Journal of Political Science, vol. 28,
       no. 1, p. 41, 1984.  :doi:`10.2307/2110786`

    Examples
    --------
    Voter 0 loves Candidates A (index 0) and B (index 1), but hates C (2).
    Voter 1 dislikes A, likes B, and loves C.
    Voter 2 hates A, and is lukewarm about B and C.

    >>> utilities = [[1.0, 1.0, 0.0],
    ...              [0.1, 0.8, 1.0],
    ...              [0.0, 0.5, 0.5],
    ...              ]

    Candidate B (1) has the highest overall support and is the utility winner:

    >>> utility_winner(utilities)
    1
    """
    utilities = np.asarray(utilities)

    # Tally all utilities
    total_utilities = utilities.sum(0).tolist()

    # Find the set of candidates who have the highest score (usually only one)
    highest = max(total_utilities)
    winners = _all_indices(total_utilities, highest)

    # Break any ties using specified method
    tiebreak = _get_tiebreak(tiebreaker, _tiebreak_map)
    return tiebreak(winners)[0]
