# coding=utf-8
# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import absolute_import, division, print_function, unicode_literals

import os

from pants.backend.native.config.environment import Linker, LLVMCppToolchain, Platform
from pants.backend.native.subsystems.native_toolchain import NativeToolchain
from pants.backend.native.targets.native_artifact import NativeArtifact
from pants.backend.native.targets.native_library import NativeLibrary
from pants.backend.native.tasks.native_compile import NativeTargetDependencies, ObjectFiles
from pants.backend.native.tasks.native_external_library_fetch import NativeExternalLibraryFiles
from pants.backend.native.tasks.native_task import NativeTask
from pants.base.exceptions import TaskError
from pants.base.workunit import WorkUnit, WorkUnitLabel
from pants.util.collections import assert_single_element
from pants.util.memo import memoized_property
from pants.util.objects import datatype
from pants.util.process_handler import subprocess


class SharedLibrary(datatype(['name', 'path'])): pass


class LinkSharedLibraryRequest(datatype([
    ('linker', Linker),
    ('object_files', tuple),
    ('native_artifact', NativeArtifact),
    'output_dir',
    ('external_lib_dirs', tuple),
    ('external_lib_names', tuple),
])):

  @classmethod
  def with_external_libs_product(cls, external_libs_product=None, *args, **kwargs):
    if external_libs_product is None:
      lib_dirs = ()
      lib_names = ()
    else:
      lib_dirs = (external_libs_product.lib_dir,)
      lib_names = external_libs_product.lib_names

    return cls(*args, external_lib_dirs=lib_dirs, external_lib_names=lib_names, **kwargs)


class LinkSharedLibraries(NativeTask):

  options_scope = 'link-shared-libraries'

  @classmethod
  def product_types(cls):
    return [SharedLibrary]

  @classmethod
  def prepare(cls, options, round_manager):
    super(LinkSharedLibraries, cls).prepare(options, round_manager)
    round_manager.require(NativeTargetDependencies)
    round_manager.require(ObjectFiles)
    round_manager.optional_product(NativeExternalLibraryFiles)

  @property
  def cache_target_dirs(self):
    return True

  @classmethod
  def implementation_version(cls):
    return super(LinkSharedLibraries, cls).implementation_version() + [('LinkSharedLibraries', 0)]

  class LinkSharedLibrariesError(TaskError): pass

  @classmethod
  def subsystem_dependencies(cls):
    return super(LinkSharedLibraries, cls).subsystem_dependencies() + (NativeToolchain.scoped(cls),)

  @memoized_property
  def _native_toolchain(self):
    return NativeToolchain.scoped_instance(self)

  @memoized_property
  def _cpp_toolchain(self):
    return self._request_single(LLVMCppToolchain, self._native_toolchain).cpp_toolchain

  @memoized_property
  def linker(self):
    return self._cpp_toolchain.cpp_linker

  @memoized_property
  def platform(self):
    # FIXME: convert this to a v2 engine dependency injection.
    return Platform.create()

  def _retrieve_single_product_at_target_base(self, product_mapping, target):
    self.context.log.debug("product_mapping: {}".format(product_mapping))
    self.context.log.debug("target: {}".format(target))