#!/usr/bin/env python3
# -*- coding: utf8 -*-

# Copyright (C) 2012-2016  Xyne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# (version 2) as published by the Free Software Foundation.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import argparse
import AUR.RPC
import logging
import os
import platform
import pm2ml
import pycman
import sys
import urllib.error
import XCGF
import XCPF



TTL = 5
SECONDS_PER_MINUTE = 60



def get_aur_pkgs(aur, pkgnames, maintainer=None):
  '''
  Search the AUR.
  '''
  if pkgnames:
    for p in AUR.RPC.insert_full_urls(aur.info(pkgnames)):
      yield p
  if maintainer:
    for p in AUR.RPC.insert_full_urls(aur.msearch(maintainer)):
      yield p



def get_upgradable(config=None, aur=None, aur_only=False):
  '''
  Determine upgradable AUR.RPC packages.
  '''
  args = ['-u']
  if config:
    args.extend(('-c', config))
  if aur is not None:
    args.append('--aur')
    if aur_only:
      args.append('--aur-only')

  pm2ml_pargs = pm2ml.parse_args(args)
  pm2ml_obj = pm2ml.Pm2ml(pm2ml_pargs)

  sync_pkgs, sync_deps, \
  aur_pkgs, aur_deps, \
  not_found, unknown_deps, orphans = pm2ml_obj.resolve_targets_from_arguments()

  for p in sync_pkgs | sync_deps:
    yield p.name
  for p in aur_pkgs | aur_deps:
    yield p['Name']



# def get_pkgbases(config, ttl, pkgnames):
#   '''
#   Try to resolve package names to package bases.
#   '''
#   handle = pycman.config.init_with_config(config)
#   sync_dbs = handle.get_syncdbs()
#
#   dbpath = XCPF.pkginfo_dbpath()
#   f = XCPF.get_memoized_archlinux_org_pkg_info_function(dbpath, ttl)
#
#   for pkgname in pkgnames:
#     pkg, data = XCPF.get_sync_pkg_info(sync_dbs, pkgname, info_retriever=f)
#     try:
#       pkgbase = data['pkgbase']
#     except (TypeError, KeyError):
#       yield pkgname
#     else:
#       logging.info('resolved {} to {}'.format(pkgname, pkgbase))
#       yield pkgbase





parser = argparse.ArgumentParser(
  prog='pbget',
  formatter_class=argparse.RawDescriptionHelpFormatter,
  description='Retrieve PKGBUILDs and local source files from ABS and the AUR for makepkg.',
  epilog='''optional dependencies:
rsync                 Required for retrieving ABS files.
''',
)
parser.add_argument(
  'pkgnames', metavar='<pkgname>', nargs='*',
  help="The source packages to retrieve."
)
parser.add_argument(
  '--abs', action='store_true',
  help='Check the ABS tree instead of the Git interface. (requires rsync)'
)
parser.add_argument(
  '--arch', metavar='<architecture>',
  help='Set the desired package architecture.'
)
parser.add_argument(
  '--config', metavar='<path>', default='/etc/pacman.conf',
  help='Pacman configuration file. Default: %(default)s'
)
parser.add_argument(
  '--aur', action='store_true',
  help='Search the AUR. (requires python3-aur)'
)
parser.add_argument(
  '--aur-only', action='store_true',
  help='Only search the AUR. (requires python3-aur)'
)
parser.add_argument(
  '--dir', metavar='<path>', default='.',
  help='Set the output directory. Default: %(default)s'
)
parser.add_argument(
  '--upgradable', action='store_true',
  help='Search for all upgradable packages. (requires pyalpm and python3-aur)'
)
parser.add_argument(
  '--resolve-pkgbases', action='store_true',
  help='Attempt to resolve package bases. This requires additional remote queries to the archlinux.org server and should only be used when necessary.'
)
parser.add_argument(
  '--maintainer', metavar='<maintainer>',
  help='Retrieve all AUR packages from the given maintainer. (implies --aur, requires python3-aur, may be extended to support official packages later)'
)
parser.add_argument(
  '--testing', action='store_true',
  help='Search the testing branches of the ABS tree.'
)
parser.add_argument(
  '--ttl', type=int, default=TTL,
  help='The cache time-to-live, in minutes. Default: %(default)s'
)
parser.add_argument(
  '--debug', action='store_true',
  help='Display debugging messages.'
)




def main(args=None):
  pargs = parser.parse_args(args)

  if pargs.debug:
    log_level = logging.DEBUG
  else:
    log_level = logging.INFO
  XCGF.configure_logging(level=log_level)

  if pargs.aur_only:
    pargs.aur = True

  if pargs.testing:
    pargs.abs = True

  if not pargs.arch:
    pargs.arch = platform.machine()

  if pargs.aur:
    aur = AUR.RPC.AUR(ttl=pargs.ttl * SECONDS_PER_MINUTE)
  else:
    aur = None

  output_dir = pargs.dir
  os.makedirs(output_dir, exist_ok=True)

  pkgnames = set(pargs.pkgnames)
  if pargs.upgradable:
    pkgnames |= set(get_upgradable(
      config=pargs.config,
      aur=aur,
      aur_only=pargs.aur_only
    ))

  if not pkgnames:
    # TODO
    # Remove this restriction when I have a scraper for
    #   https://www.archlinux.org/packages/?packager=
    if pargs.aur and pargs.maintainer:
      pargs.aur_only = True
    else:
      logging.info('nothing to do')
      return

  if not pargs.aur_only:
#     if pargs.resolve_pkgbases:
#       pkgnames = set(get_pkgbases(
#         pargs.config,
#         pargs.ttl * SECONDS_PER_MINUTE,
#         pkgnames
#       ))
    opi = XCPF.OfficialPkgInfo(
      config=pargs.config,
      arch=pargs.arch,
      testing=pargs.testing,
      ttl=pargs.ttl*SECONDS_PER_MINUTE,
      log_level=logging.INFO,
      resolve_pkgbases=pargs.resolve_pkgbases,
    )

    if pargs.abs:
      logging.info('searching ABS rsync server')
      for pkgname in opi.retrieve_abs_via_rsync(output_dir, pkgnames):
        logging.info('found {}'.format(pkgname))
        pkgnames.remove(pkgname)

    else:
      logging.info('searching ABS Git interface')
      for pkgname, url in opi.retrieve_abs_via_git(output_dir, pkgnames):
          logging.info('found {} at {}'.format(pkgname, url))
          pkgnames.remove(pkgname)

  if pargs.aur and (pkgnames or pargs.maintainer):
    logging.info('searching AUR')
    aurpkgs = list(get_aur_pkgs(aur, pkgnames, maintainer=pargs.maintainer))
    names = set(p['Name'] for p in aurpkgs)
    pkgnames -= (names | set(n.lower() for n in names))
    for aurpkg in aurpkgs:
      # One at a time to display progress continuously instead of waiting for
      # all of them to complete.
      AUR.RPC.download_archives(output_dir, [aurpkg])
      logging.info('found {} at {}'.format(aurpkg['Name'], aurpkg['URLPath']))

  if pkgnames:
    for pkgname in pkgnames:
      logging.warning('{} not found'.format(pkgname))

if __name__ == '__main__':
  try:
    main()
  except KeyboardInterrupt:
    pass
  except (OSError, urllib.error.HTTPError) as e:
    sys.exit(str(e))
