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

import logging
import urllib

from google.appengine.api import urlfetch

from common.buildbot import build
from common.buildbot import network


class Builds(object):

  def __init__(self, master_name, builder_name, url):
    self._master_name = master_name
    self._builder_name = builder_name
    self._url = url

  def __getitem__(self, key):
    """Fetches a Build object containing build details.

    Args:
      key: A nonnegative build number.

    Returns:
      A Build object.

    Raises:
      TypeError: key is not an int.
      ValueError: key is negative.
    """
    # We can't take slices because we don't have a defined length.
    if not isinstance(key, int):
      raise TypeError('build numbers must be integers, not %s' %
                      type(key).__name__)

    return self.Fetch((key,))

  def Fetch(self, build_numbers):
    """Downloads and returns build details.

    If a build has corrupt data, it is not included in the result. If you
    strictly need all the builds requested, be sure to check the result length.

    Args:
      build_numbers: An iterable of build numbers to download.

    Yields:
      Build objects, in the order requested. Some may be missing.

    Raises:
      ValueError: A build number is invalid.
    """
    if not build_numbers:
      return

    for build_number in build_numbers:
      if build_number < 0:
        raise ValueError('Invalid build number: %d' % build_number)

    build_query = urllib.urlencode(
        [('select', build_number) for build_number in build_numbers])
    url = 'json/builders/%s/builds/?%s' % (
        urllib.quote(self._builder_name), build_query)
    url = network.BuildUrl(self._master_name, url)
    try:
      builds = network.FetchData(url).values()
    except (ValueError, urlfetch.ResponseTooLargeError):
      # The JSON decode failed, or the data was too large.
      # Try downloading the builds individually instead.
      builds = []
      for build_number in build_numbers:
        url = 'json/builders/%s/builds/%d' % (
            urllib.quote(self._builder_name), build_number)
        url = network.BuildUrl(self._master_name, url)
        try:
          builds.append(network.FetchData(url))
        except (ValueError, urlfetch.ResponseTooLargeError):
          logging.warning('Unable to fetch %s build %d',
                          self._master_name, build_number)
          continue

    for build_data in builds:
      if 'error' in build_data:
        continue
      yield build.Build(build_data, self._url)