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

"""Functions to work with Miniconda python environment.

See http://conda.pydata.org/miniconda.html
"""

from recipe_engine import recipe_api


class CondaEnv(object):
  def __init__(self, module_api, version, path):
    self._module_api = module_api
    self.version = version
    self.path = path
    self._wiped = False

  def __enter__(self, *_args):
    return self

  def __exit__(self, *_args):
    self.wipe()

  @property
  def conda_exe(self):
    """Path to 'conda' executable."""
    if self._module_api.m.platform.is_win:
      return self.path.join('Scripts', 'conda.exe')
    return self.path.join('bin', 'conda')

  def install(self, pkg):
    """Installs a conda package into the environment."""
    return self._call(['install', pkg])

  def convert_to_cipd_package(self, package_name, output_file):
    """Packages Conda environment as CIPD package.

    It also breaks it in the process (by irreversibly mutating it to be
    prefix independent, as much as possible). It is not possible to install
    new packages into the environment once it has been mutated.

    Args:
      package_name: name of the CIPD package, 'infra/conda_python/linux-amd64'.
      output_file: path to put *.cipd package to.
    """
    self._call(['clean', '--tarballs', '--index-cache', '--packages'])
    self._module_api.m.python(
        'make conda env location independent',
        self._module_api.resource('butcher_conda.py'),
        args=[self.path])
    self._module_api.m.cipd.build(
        input_dir=self.path,
        output_package=output_file,
        package_name=package_name,
        install_mode='copy')

  def wipe(self):
    """Wipes the directory with Conda installation."""
    if not self._wiped:
      self._wiped = True
      self._module_api.m.file.rmtree('removing conda', self.path)

  def _call(self, cmd):
    with self._module_api.m.context(env={'PYTHONPATH': None}):
      return self._module_api.m.step(
          ' '.join(['conda'] + cmd),
          [self.conda_exe] + cmd + ['--yes'])


class CondaApi(recipe_api.RecipeApi):
  def install(self, version, path):
    """Downloads Miniconda installer for given version and executes it.

    Args:
      version: version of Miniconda to install, e.g. 'Miniconda2-3.18.3'.
      path: prefix to install Miniconda into.

    Returns:
      Instance of CondaEnv, that also optionally acts as context manager that
      deletes the environment on exit.
    """
    # Construct URL to installer. See https://repo.continuum.io/miniconda/.
    os = {
      'linux': 'Linux',
      'mac': 'MacOSX',
      'win': 'Windows',
    }[self.m.platform.name]
    arch = {
      32: 'x86',
      64: 'x86_64',
    }[self.m.platform.bits]
    ext = '.exe' if self.m.platform.is_win else '.sh'
    url = (
        'https://repo.continuum.io/miniconda/%s-%s-%s%s' %
        (version, os, arch, ext))

    # Fetch installer into temp directory and install Conda to 'path'.
    # We acknowledge the license agreement.
    tmp = self.m.path.mkdtemp('conda')
    installer = tmp.join(url[url.rfind('/')+1:])
    try:
      self.m.url.get_file(
          url,
          installer,
          step_name='fetch miniconda installer')
      # See http://conda.pydata.org/docs/help/silent.html
      if self.m.platform.is_win:
        install_cmd = [
          installer, '/InstallationType=JustMe', '/AddToPath=0',
          '/RegisterPython=0', '/S', '/D=' + str(path),
        ]
      else:
        install_cmd = ['/bin/bash', installer, '-b', '-p', path]
      with self.m.context(env={'PYTHONPATH': ''}):
        self.m.step('install miniconda', install_cmd)
      return CondaEnv(self, version, path)
    finally:
      self.m.file.rmtree('remove miniconda installer', tmp)
