# Copyright (c) 2014 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

from webports.error import Error
from webports import configuration, pkg_info, util

EXTRA_KEYS = ['BUILD_CONFIG', 'BUILD_ARCH', 'BUILD_TOOLCHAIN',
              'BUILD_SDK_VERSION', 'BUILD_NACLPORTS_REVISION']

class Package(object):
  extra_keys = []
  list_props = (
    'DEPENDS',
    'CONFLICTS',
    'DISABLED_ARCH',
    'DISABLED_LIBC',
    'DISABLED_TOOLCHAIN'
  )

  def __init__(self, info_file=None):
    self.info = info_file
    if self.info:
      with open(self.info) as f:
        self.ParseInfo(f.read())

  def ParseInfo(self, info_string):
    valid_keys = pkg_info.VALID_KEYS + self.extra_keys
    required_keys = pkg_info.REQUIRED_KEYS + self.extra_keys

    for key in valid_keys:
      if key in self.list_props:
        setattr(self, key, [])
      else:
        setattr(self, key, None)

    # Parse pkg_info file
    info = pkg_info.ParsePkgInfo(info_string, self.info, valid_keys,
                                 required_keys)

    # Set attributres based on pkg_info setttings.
    for key, value in info.items():
      setattr(self, key, value)

    self.Validate()

  def Validate(self):
    for libc in self.DISABLED_LIBC:
      if libc not in configuration.VALID_LIBC:
        raise Error('%s: invalid libc: %s' % (self.info, libc))

    for toolchain in self.DISABLED_TOOLCHAIN:
      if '/' in toolchain:
        toolchain, arch = toolchain.split('/')
        if arch not in util.arch_to_pkgarch:
          raise Error('%s: invalid architecture: %s' % (self.info, arch))
      if toolchain not in configuration.VALID_TOOLCHAINS:
        raise Error('%s: invalid toolchain: %s' % (self.info, toolchain))

    for arch in self.DISABLED_ARCH:
      if arch not in util.arch_to_pkgarch:
        raise Error('%s: invalid architecture: %s' % (self.info, arch))

    if '_' in self.NAME:
      raise Error('%s: package NAME cannot contain underscores' % self.info)

    if self.NAME != self.NAME.lower():
      raise Error('%s: package NAME cannot contain uppercase characters' %
                  self.info)

    if '_' in self.VERSION:
      raise Error('%s: package VERSION cannot contain underscores' % self.info)

    if self.DISABLED_ARCH and self.ARCH is not None:
      raise Error('%s: contains both ARCH and DISABLED_ARCH' % self.info)

    if self.DISABLED_LIBC and self.LIBC is not None:
      raise Error('%s: contains both LIBC and DISABLED_LIBC' % self.info)

  def __cmp__(self, other):
    return cmp((self.NAME, self.VERSION, self.config),
               (other.NAME, other.VERSION, other.config))

  def __hash__(self):
    return hash((self.NAME, self.VERSION, self.config))

  def __str__(self):
    return '<Package %s %s %s>' % (self.NAME, self.VERSION, self.config)

  def InfoString(self):
    return "'%s' [%s]" % (self.NAME, self.config)

  def LogStatus(self, message, suffix=''):
    util.LogHeading(message, " '%s' [%s] %s" %
                    (util.Color(self.NAME, 'yellow'),
                     util.Color(self.config, 'blue'), suffix))

  def CheckDeps(self, valid_packages):
    for package in self.DEPENDS:
      if package not in valid_packages:
        util.Log('%s: Invalid dependency: %s' % (self.info, package))
        return False

    for package in self.CONFLICTS:
      if package not in valid_packages:
        util.Log('%s: Invalid conflict: %s' % (self.info, package))
        return False

    return True

  def IsAnyVersionInstalled(self):
    return util.IsInstalled(self.NAME, self.config)

  def GetInstallStamp(self):
    """Returns the name of install stamp for this package."""
    return util.GetInstallStamp(self.NAME, self.config)

  def GetListFile(self):
    """Returns the name of the installed file list for this package."""
    return util.GetListFile(self.NAME, self.config)

