import hashlib
import logging

from lib.python import common_constants
from lib.python import opencsw
from lib.python import representations
from lib.python import sharedlib_utils

def ComposeFakeSrv4Md5(pkgname, osrel, arch):
  """Returns a fake md5 sum of a fake srv4 package.

  For the purposes of fake srv4 packages for SUNW stuff.
  """
  key = pkgname + osrel + arch
  md5_sum = hashlib.md5(key).hexdigest()
  return md5_sum


def CreateFakeElfdumpInfo(soname):
  return {
      'version definition': [],
      'version needed': [],
      'symbol table': [
        representations.ElfSymInfo(soname=soname, symbol='foo',
                                   flags='DBL', shndx='UNDEF',
                                   bind='GLOB', version=None)]}


class PkgstatsListComposer(object):
  """Reponsible for creating lists of pkgstats objects."""
  
  def __init__(self, osrel, arch):
    self.osrel = osrel
    self.arch = arch
    self.pkgstats_by_pkgname = {}
    self.elfdump_info = {}

  def AddPkgname(self, pkgname):
    "An idempotent function creating a package if it doesn't exist yet."
    logging.debug("PkgstatsListComposer.AddPkgname(%r)", pkgname)
    if pkgname not in self.pkgstats_by_pkgname:
      self.pkgstats_by_pkgname[pkgname] = self._GetTemplate(pkgname)

  def _GetTemplate(self, pkgname):
    """Returns a template data structure."""
    version = 'fakeversion'
    md5_sum = ComposeFakeSrv4Md5(pkgname, self.osrel, self.arch)
    catalogname = opencsw.PkgnameToCatName(pkgname)
    rev = 'REV=0000.00.00'
    basename = ('%s-%s,%s-%s-%s-FAKE.pkg.gz'
                % (catalogname, version, rev, self.osrel, self.arch))
    pkgchk = {
        'return_code': 0,
        'stderr_lines': [],
        'stdout_lines': ["This is a fake package"]}
    basic_stats = {
        'catalogname': catalogname,
        'md5_sum': md5_sum,
        'parsed_basename': {
          'arch': self.arch,
          'catalogname': None,
          'full_version_string': version + "," + rev,
          'osrel': self.osrel,
          'revision_info': {
            'REV': 'REV=0000.00.00',
          },
          'vendortag': None,
          'version': version,
          'version_info': {
            'major version': '0',
            'minor version': '0',
            'patchlevel': '0',
          },
        },
        'pkg_basename': basename,
        'pkg_path': None,
        'pkgname': pkgname,
        'size': 0,
        'stats_version': 10L}
    binaries_dump_info = []
    files_metadata = []
    binaries_list = []
    # Isalist is based on package architecture.
    isalist = []
    if self.arch in common_constants.ARCHITECTURES:
      if self.arch in common_constants.PHYSICAL_ARCHITECTURES:
        isalist = common_constants.ISALISTS_BY_ARCH[self.arch]
      else:
        isalist = ["archall package doesn't have isalist"]
    else:
      raise PackageError("Architecture %s found. Expected one of %s.",
                         self.arch, common_constants.ARCHITECTURES)
    isalist = list(isalist)
    pkginfo = {
        'ARCH': self.arch,
        'CATEGORY': 'fake-package',
        'CLASSES': 'none',
        'EMAIL': 'fake-maintainer@opencsw.org',
        'HOTLINE': 'No hotline',
        'NAME': 'A fake, autogenerated package',
        'OPENCSW_CATALOGNAME': catalogname,
        'PKG': pkgname,
        'PSTAMP': 'fake@unknown-00000000000000',
        'VENDOR': 'A fake package packaged for CSW by The Buildfarm',
        'VERSION': 'fake_version',
    }
    pkgmap = []
    tmpl = {
        'bad_paths': {},
        'basic_stats': basic_stats,
        'binaries': binaries_list,
        'binaries_dump_info': binaries_dump_info,
        'binary_md5_sums': [],
        'depends': [],
        'i_depends': [],
        'files_metadata': files_metadata,
        'isalist': isalist,
        'mtime': '2010-07-05T23:48:10',
        'overrides': [],
        'pkgchk': pkgchk,
        'pkginfo': pkginfo,
        'pkgmap': pkgmap,
    }
    return tmpl

  def GetPkgstats(self):
    return self.pkgstats_by_pkgname.values()

  def FakeBinaryMd5(self, pkgname, binary_path):
    h = hashlib.md5()
    h.update(pkgname)
    h.update(binary_path)
    return h.hexdigest()

  def AddFile(self, pkgname, pkgmap_entry, file_metadata,
              binary_dump_info, elfinfo):
    if pkgname not in self.pkgstats_by_pkgname:
      raise DataError('%r not in self.pkgstats_by_pkgname.' % pkgname)
    pkgstats = self.pkgstats_by_pkgname[pkgname]
    pkgmap = pkgstats['pkgmap']
    pkgmap.append(pkgmap_entry)
    # Metadata require paths with no leading slash.
    file_path = pkgmap_entry.path
    if pkgmap_entry.path.startswith('/'):
      file_path = file_path[1:]
    local_metadata = file_metadata._asdict()
    local_metadata['path'] = file_path
    if local_metadata['mime_type'] is not None:
      if sharedlib_utils.IsBinary(local_metadata):
        pkgstats['binaries'].append(local_metadata['path'])
    # We need to cast the dict to a named tuple to make sure the field
    # oreder is correct.
    local_metadata = representations.FileMetadata(**local_metadata)
    # Then we can cast to a tuple.
    local_metadata = tuple(local_metadata)
    pkgstats['files_metadata'].append(local_metadata)
    if binary_dump_info:
      fake_binary_md5 = self.FakeBinaryMd5(pkgname, binary_dump_info.path)
      pkgstats['binaries_dump_info'].append(tuple(binary_dump_info))
      pkgstats['binary_md5_sums'].append((binary_dump_info.path, fake_binary_md5))
    if elfinfo:
      self.elfdump_info[fake_binary_md5] = elfinfo
    elif binary_dump_info:
      self.elfdump_info[fake_binary_md5] = CreateFakeElfdumpInfo('libfoo.so.1')

  def GetElfdumpInfo(self, md5_sum):
    return self.elfdump_info[md5_sum]
