# 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.

class DependencyInfo(object):
  def __init__(self, dependency, platform, config_path, local_path_info=None,
               cloud_storage_info=None):
    """ Container for the information needed for each dependency/platform pair
    in the dependency_manager.

    Args:
        Required:
          dependency: Name of the dependency.
          platform: Name of the platform to be run on.
          config_path: Path to the config_path this information came from. Used
                       for error messages to improve debugging.

        Optional:
          local_path_info: A LocalPathInfo instance.
          cloud_storage_info: An instance of CloudStorageInfo.
    """
    # TODO(aiolos): update the above doc string for A) the usage of zip files
    # and B) supporting lists of local_paths to be checked for most recently
    # changed files.
    if not dependency or not platform:
      raise ValueError(
          'Must supply both a dependency and platform to DependencyInfo')

    self._dependency = dependency
    self._platform = platform
    self._config_paths = [config_path]
    self._local_path_info = local_path_info
    self._cloud_storage_info = cloud_storage_info

  def Update(self, new_dep_info):
    """Add the information from |new_dep_info| to this instance.
    """
    self._config_paths.extend(new_dep_info.config_paths)
    if (self.dependency != new_dep_info.dependency or
        self.platform != new_dep_info.platform):
      raise ValueError(
          'Cannot update DependencyInfo with different dependency or platform.'
          'Existing dep: %s, existing platform: %s. New dep: %s, new platform:'
          '%s. Config_paths conflicting: %s' % (
              self.dependency, self.platform, new_dep_info.dependency,
              new_dep_info.platform, self.config_paths))
    if new_dep_info.has_cloud_storage_info:
      if self.has_cloud_storage_info:
        raise ValueError(
            'Overriding cloud storage data is not allowed when updating a '
            'DependencyInfo. Conflict in dependency %s on platform %s in '
            'config_paths: %s.' % (self.dependency, self.platform,
                                   self.config_paths))
      else:
        self._cloud_storage_info = new_dep_info._cloud_storage_info
    if not self._local_path_info:
      self._local_path_info = new_dep_info._local_path_info
    else:
      self._local_path_info.Update(new_dep_info._local_path_info)

  def GetRemotePath(self):
    """Gets the path to a downloaded version of the dependency.

    May not download the file if it has already been downloaded.
    Will unzip the downloaded file if specified in the config
    via unzipped_hash.

    Returns: A path to an executable that was stored in cloud_storage, or None
       if not found.

    Raises:
        CredentialsError: If cloud_storage credentials aren't configured.
        PermissionError: If cloud_storage credentials are configured, but not
            with an account that has permission to download the needed file.
        NotFoundError: If the needed file does not exist where expected in
            cloud_storage or the downloaded zip file.
        ServerError: If an internal server error is hit while downloading the
            needed file.
        CloudStorageError: If another error occured while downloading the remote
            path.
        FileNotFoundError: If the download was otherwise unsuccessful.
    """
    if self.has_cloud_storage_info:
      return self._cloud_storage_info.GetRemotePath()
    return None

  def GetRemotePathVersion(self):
    if self.has_cloud_storage_info:
      return self._cloud_storage_info.version_in_cs
    return None

  def GetLocalPath(self):
    """Gets the path to a local version of the dependency.

    Returns: A path to a local dependency, or None if not found.

    """
    if self.has_local_path_info:
      return self._local_path_info.GetLocalPath()
    return None

  @property
  def dependency(self):
    return self._dependency

  @property
  def platform(self):
    return self._platform

  @property
  def config_paths(self):
    return self._config_paths

  @property
  def local_path_info(self):
    return self._local_path_info

  @property
  def has_cloud_storage_info(self):
    return bool(self._cloud_storage_info)

  @property
  def has_local_path_info(self):
    return bool(self._local_path_info)

  @property
  def cloud_storage_info(self):
    return self._cloud_storage_info
