#!/usr/bin/python3

# {{{ Imports
import sys
import os.path
from urllib.request import urlopen
import argparse
from xml.etree.cElementTree import fromstring as xmlParser
import traceback
# }}}

# {{{ Constants
_NAME="WeeChatScriptsHelper"
_DESC="Weechat Scripts Packaging Helper"
_VERSION="1.0"
REMOTE_LIST="https://weechat.org/files/plugins.xml"
MIN_VERSION="0.3.0"

REWRITE_LICENSES = {
    'Apache-2.0': 'Apache-2',
    'Artistic-2.0': 'Artistic-2',
    'Beerware': 'Beerware',
    '0BSD': 'BSD-0',
    'BSD-2-Clause': 'BSD-2c',
    'BSD-3-Clause': 'BSD-3c',
    'CC0-1.0': 'CC0-1.0',
    'GPL-2.0-only': 'GPL-2',
    'GPL-2.0-or-later': 'GPL-2+',
    'GPL-3.0-only': 'GPL-3',
    'GPL-3.0-or-later': 'GPL-3+',
    'ISC': 'ISC',
    'MIT': 'MIT',
    'MIT-0': 'MIT-0',
    'Public-domain': 'public-domain',
    'WTFPL': 'WTFPL',
}

SUPPORTED_LICENSES = REWRITE_LICENSES.values()
# }}}

# {{{  Logger
class Logger(object):

    _COLOR_INFO = '92'
    _COLOR_WARN = '93'
    _COLOR_DEBUG = '94'
    _COLOR_ERROR = '91'

    def __init__(self, verbose=False, debug=False):
        self.is_verbose = verbose
        self.is_debug = debug
        self.errors = 0
        self.out = sys.stdout

    def _prefix(self, msgtype):
        prefix = '[%s]' % (msgtype)
        if self.out.isatty():
            prefix = '[\033[0;%sm%s\033[0;m]' % (getattr(self, '_COLOR_' + msgtype.upper()), msgtype)
        return prefix

    def use_stderr(self):
        self.out = sys.stderr

    def info(self, msg):
        if self.is_verbose:
            self.out.write('%s %s\n' % (self._prefix('info'), msg))

    def warn(self, msg):
        if self.is_verbose:
            self.out.write('%s %s\n' % (self._prefix('warn'), msg))

    def debug(self, msg):
        if self.is_debug:
            self.out.write('%s %s\n' % (self._prefix('debug'), msg))

    def error(self, msg):
        self.errors += 1
        self.out.write('%s %s\n' % (self._prefix('error'), msg))
# }}}

# {{{ WeechatScripts
class WeechatScripts(object):

    def __init__(self, options, logger=None):
        self._logger = logger if logger is not None else Logger()
        self._options = options

    def get_xmldom(self):
        xmldom = None
        try:
            xmlstr = urlopen(REMOTE_LIST).read()
        except:
            xmlstr = None
            self._logger.error("Unable to retrieve '%s'" % (REMOTE_LIST))
        if xmlstr:
            try:
                xmldom = xmlParser(xmlstr)
            except:
                self._logger.error("Unable to parse XML")
                xmldom = None
        return xmldom

    def get_license(self, license):
        if license in REWRITE_LICENSES:
            license = REWRITE_LICENSES[license]
        return license

    def dfsg_license(self, license):
        return license in SUPPORTED_LICENSES

    def get_datas(self):
        scripts = {}
        licenses = []
        xmldom = self.get_xmldom()
        if xmldom is not None:
            for el in xmldom.findall('plugin'):
                max_vers = el.find('max_weechat').text
                if max_vers is not None and max_vers < MIN_VERSION:
                    continue

                lang = el.find('language').text
                filename = el.find('url').text.split('/')[-1]
                author = "%s <%s>" % (el.find('author').text, el.find('mail').text)
                tags = el.find('tags').text.split(',')

                if lang == 'python' and 'py3' not in tags:
                    self._logger.warn("Python script '%s' is not compatible with Python3" 
                                        % (os.path.basename(filename)))
                    continue

                date = ''
                date_add = int(el.find('added').text.split('-')[0])
                if el.find('updated').text == None:
                   date = "%d" % (date_add)
                else:
                    date_upd = int(el.find('updated').text.split('-')[0])
                    if date_upd > date_add:
                        date = "%d-%d" % (date_add, date_upd)
                    else:
                        date = "%d" % (date_add)

                license = self.get_license(el.find('license').text)
                dfsg_license = self.dfsg_license(license)

                if not dfsg_license:
                    self._logger.warn("Script '%s' has a not DFSG compliant lisense: %s" 
                                        % (os.path.basename(filename), license))
                    continue

                if not license in licenses:
                    licenses.append(license)

                if not lang in scripts:
                    scripts[lang] = []
                scripts[lang].append({
                        'language': el.find('language').text,
                        'url': el.find('url').text,
                        'file': filename,
                        'version': el.find('version').text,
                        'license': license,
                        'author': author,
                        'date': date,
                        })
        return scripts, licenses

    def short_license(self, license):
        content = ''
        f = "%s/licenses/%s" % (os.path.dirname(__file__), license)
        if os.path.isfile(f):
            content = open(f).read()
        else:
            self._logger.error("Unable to find short license '%s' in %s" %
                                (license, f))
        return content

    def copyright_header(self):
        content = ''
        f = "%s/copyright.in" % (os.path.dirname(__file__))
        if os.path.isfile(f):
            content = open(f).read()
        return content

    def copyright(self):
        self._logger.use_stderr()
        scripts, licenses = self.get_datas()
        sys.stdout.write("%s\n" % self.copyright_header())
        if scripts != {}:
            for l in sorted(scripts.keys()):
                for script in scripts[l]:
                    sys.stdout.write("Files: %s/%s\n" % (l.lower(), script['file']))
                    sys.stdout.write("Copyright: %s, %s\n" % (script['date'], script['author']))
                    sys.stdout.write("License: %s\n\n" % (script['license']))
        if licenses != []:
            for license in sorted(licenses):
                sys.stdout.write("License: %s\n" % (license))
                sys.stdout.write("%s\n" % (self.short_license(license)))

    def wget(self, url, path):
        ret = False
        content = None
        try:
            content = urlopen(url).read()
        except Exception as e:
            self._logger.error("Unable to retrieve '%s': %s" % (url, e))
            content = None

        if content:
            try:
                f = open(path, 'w')
                f.write(content.decode('utf-8'))
                f.close()
                ret = True
            except Exception as e:
                self._logger.error("Unable to write file '%s': %s" % (path, e))

        return ret

    def download(self):
        scriptlist, _ = self.get_datas()
        if scriptlist != {}:
            for l in scriptlist:
                for script in scriptlist[l]:
                    path = "%s/%s" % (script['language'], script['file'])
                    if not os.path.exists(script['language']):
                        os.mkdir(script['language'])
                    if self.wget(script['url'], path):
                        self._logger.info("Success while saving '%s' %s (%s)"
                                            % (path, script['version'], script['license']))
                    else:
                        self._logger.error("Failure while saving '%s'" % (path))
# }}}

# {{{ main
def main():
    parser = argparse.ArgumentParser(prog=_NAME, description=_DESC)
    parser.add_argument('--version', action='version', version='%(prog)s ' + _VERSION )
    parser.add_argument(
            '-v', '--verbose', dest='verbose',
            default=False, action='store_true',
            help='Be verbose during process (default: no)')
    parser.add_argument(
            '-d', '--debug', dest='debug',
            default=False, action='store_true',
            help='Print additional debug informations: traceback, etc. (default: no)')
    parser.add_argument(
            '-a', '--action', dest='action',
            choices=('download', 'copyright'), default=None,
            help='Action to realize')
    options = parser.parse_args()

    logger = Logger(verbose=options.verbose or options.debug, debug=options.debug)
    try:
        ws = WeechatScripts(options=options, logger=logger)
        getattr(ws, options.action)()
    except Exception as e:
        if not options.debug:
            logger.error('Exception raised (use --debug option to get more info): %s' % (str(e)))
        else:
            logger.error('Exception raised: %s' % (e))
            traceback.print_exc(file=sys.stderr)
        sys.exit(-1)
    else:
        if logger.errors > 0:
            sys.exit(-3)
# }}}

# {{{ __main__
if __name__ == "__main__":
    main()
# }}}

# vim: foldmethod=marker foldlevel=0 foldenable
