#!/usr/bin/env python
import os
import sys

def print_help(exit_code=None, message=None):
    print('''Syntax: %s <JSON configuration file>

Sample configuration file:

-----8<---snip----8<--
{
    "user": "<your AUR login>",
    "lowercase": [ "ZConfig", "Ghost.py" ]
}
-----8<---snip----8<--

Use "lowercase" to give hints about the "official" (Pypi) package name.

Returns:
    1 if no configuration is given
    2 if the configuration is not valid
'''%sys.argv[0])

    if message: print(message)
    if exit_code: raise SystemExit(exit_code)

if len(sys.argv) != 2:
    conf_name = os.path.expanduser('~/.config/aury/config')
    conf_dir = os.path.dirname(conf_name)
    if not os.path.exists(conf_name):
        os.makedirs(conf_dir)
        open(conf_name, 'w').write('{\n"user": "<your AUR login>",\n"lowercase": []\n}\n');
        print_help(1,
        'Please give a single valid JSON file as parameter (should contain "user: <user id>")\nYou can also edit %r and restart %s'%(conf_name, sys.argv[0]))
    os.chdir(conf_dir)
else:
    if sys.argv[1].endswith('help'):
        print_help(1)
    conf_name = sys.argv[1]

import json # configuration

try:
    conf = json.load(open(conf_name))
except Exception as e:
    print_help(2,
        'ERROR: %s\n%r should be a valid JSON file.'%(e, conf_name))

print("Warming up")

from urllib import request # http
from xml.etree import cElementTree as ElementTree # xhtml parsing
import xmlrpc.client as rpc_client # pypi
import json # pypi
import tarfile, io # PKGBUILD fetching
import hashlib # md5
from urllib.parse import quote # json urls
from urllib.error import HTTPError
import itertools

class Pypi:
    def __init__(self):
        self._xml = rpc_client.ServerProxy('http://pypi.python.org/pypi')

    def last_release(self, pkg):
        r = self._xml.package_releases(pkg)
        if r:
            return r[0]

    def rel_info(self, pkg, version):
        r = self._xml.release_urls(pkg, version)
        return r

    def get_links(self, pkg, version):
        r = [ {'url': x['url'], 'md5': x['md5_digest']}
            for x in self.rel_info(pkg, version)]
        if not r:
            res = json.loads(request.urlopen('http://pypi.python.org/pypi/%s/json'%quote(pkg)).read().decode('utf-8'))
            if res['urls']:
                r = [ {'url': x['url'], 'md5': x['md5_digest']}
                    for x in res['urls']]
            elif 'download_url' in res['info']:
                r = [ {'url': res['info']['download_url'], 'md5': None} ]
            else:
                r = [ {'url': res['info']['package_url'], 'md5': None} ]
        return r

    def get_source_link(self, pkg, version):
        links = self.get_links(pkg, version)
        if not links:
            return
        for infos in links:
            if '/source/' in infos['url']:
                return infos
        for infos in links:
            if not '.egg' in infos['url']:
                return infos
        return links[0]

pkg_index = Pypi()

uri = 'https://aur.archlinux.org/packages.php?SeB=m&PP=250&L=2&K='+conf['user']

print("Connecting...")
try:
    d = request.urlopen(uri).read().decode('utf-8').replace('&nbsp;', ' ').replace('&copy;', '')
    doc = ElementTree.fromstring(d)
    del d
except HTTPError:
    print_help(3, message="Please check your %r file, login looks invalid"%conf_name)

lc_map = {x.lower():x for x in conf['lowercase']}

print('Processing:')

ns = {'x': 'http://www.w3.org/1999/xhtml'}

valid = itertools.count()
scm   = itertools.count()
total = itertools.count()
nopython= itertools.count()

for tr in doc.findall('.//x:form/x:table/x:tbody/x:tr', namespaces=ns):

    tds = list(tr.findall('x:td', namespaces=ns))
    if tds:
        next(total)
        # extract name & version
        opkg =  tds[1].find('x:a', namespaces=ns).text.strip()
        ver = tds[2].text.strip()
        ver = ver.rsplit('-', 1)[0]
        pkg = opkg

        # download the package itself
        if not os.path.exists(opkg):
            pfx = opkg[:2]
            tgz = request.urlopen('http://aur.archlinux.org/packages/{1}/{0}/{0}.tar.gz'.format(opkg, pfx)).read()
            tar = tarfile.TarFile.gzopen(opkg+'.tgz', fileobj=io.BytesIO(tgz))
            tar.extractall()

        # skip DEV packages
        if pkg.endswith('-git') or pkg.endswith('-hg'):
            next(scm)
            continue

        # skip non-python packages
        if 'python' not in open(os.path.join(opkg, 'PKGBUILD')).read():
            print(' - %s (skipping: not a standard python package)'%opkg)
            next(nopython)
            continue

        # strip prefix
        if pkg.startswith('python2-') or pkg.startswith('python-'):
            pkg = pkg.split('-', 1)[1]

        # fix the caption from the map
        if pkg in lc_map:
            pkg = lc_map[pkg]

        # display some progress information (a dot currently)
        print(" *", pkg)

        # get pypi informations
        utd_ver = pkg_index.last_release(pkg)
        if not utd_ver:
            print('Unable to find "{0}" on Pypi !,\n you may need to add this package to "lowercase" section of {1}'.format(pkg, conf_name))
            continue

        next(valid)

        # if version is not a digit, skip it
        if utd_ver[0].isalpha():
            print('Skipping bad PyPi version information for %s (%r)'%(pkg, utd_ver))
        # if it's not matching the latest version, ugrade !
        elif utd_ver != ver:
            print( "%s (%s => %s)"%(opkg, ver, utd_ver) )
            # get the md5 value
            infos = pkg_index.get_source_link(pkg, utd_ver)
            if infos['md5']:
                md5 = infos['md5']
            else:
                md5 = hashlib.md5()
                while True:
                    d = request.urlopen(infos['url']).read(2**16)
                    if not d:
                        break
                    md5.update(d)
                md5 = md5.hexdigest()
            PKGBUILD = open(os.path.join(opkg, 'PKGBUILD')).readlines()
            for i, line in enumerate(PKGBUILD):
                if line.startswith('pkgver'):
                    PKGBUILD[i] = 'pkgver=%s\n'%utd_ver
                elif line.startswith('pkgrel'):
                    PKGBUILD[i] = 'pkgrel=1\n'
                elif line.startswith('md5sums'):
                    PKGBUILD[i] = "md5sums=('%s')\n"%md5

            open(os.path.join(opkg, 'PKGBUILD'), 'w').writelines(PKGBUILD)
            os.system('cd %s && makepkg -f --source && burp %s-%s-1.src.tar.gz'%(opkg, opkg, utd_ver))

print("Processed {0} packages ({1} found, {2} rolling packages, {3} non-python).".format(next(valid), next(total), next(scm), next(nopython)))

