#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# This file is part of the Librem 5 Get Package Info tool.
# Read information about packages from dpkg and apt files.

# Copyright (C) 2019 Purism SPC
# SPDX-License-Identifier: GPL-3.0+
# Author: David Boddie <david.boddie@puri.sm>

import glob
import os

from debian import copyright, deb822


class Worker:

    def __init__(self, root_dir, output_dir):

        self.root_dir = root_dir
        self.output_dir = output_dir

    def html_escape(self, text):
        return text.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt')

    def make_link(self, href, text):

        if href:
            return '<a href="file://%s">%s</a>' % (href, self.html_escape(text))
        else:
            return self.html_escape(text)

    def read_sources(self):

        lists_dir = os.path.join(self.root_dir, 'var', 'lib', 'apt', 'lists')
        sources_files = glob.glob(os.path.join(lists_dir, '*_Sources'))

        self.sources = {}
        files = []
        total = 0

        for sources_file in sources_files:

            file_name = os.path.split(sources_file)[1]
            rest = file_name.split('_dists_')[1]
            suite_name = rest[:rest.find('_')]
            files.append((suite_name, sources_file))
            total += open(sources_file).read().count('\n\n')

        i = 0
        for suite_name, sources_file in files:

            for source in deb822.Sources.iter_paragraphs(open(sources_file)):
                names = [source['Package']]
                names += [piece.strip() for piece in source['Binary'].split(',')]

                for name in names:
                    if name not in self.sources:
                        self.sources[name] = source

                i += 1
                yield (suite_name, i, total)

    def get_source_files(self, name):

        try:
            source = self.sources[name]
        except KeyError:
            return None

        try:
            for files_key in 'Files', 'Checksums-Sha256':
                if files_key in source:
                    files = source[files_key]
                    break
            else:
                return None

            return source, files

        except KeyError:
            return None

    def get_copyright(self, name):

        path = os.path.join(self.root_dir, 'usr', 'share', 'doc', name, 'copyright')

        if os.path.exists(path):
            return os.path.join('/', 'usr', 'share', 'doc', name, 'copyright')

        return None

    def get_licenses(self, file_name):

        try:
            c = copyright.Copyright(open(os.path.join(file_name)))
        except copyright.Error:
            # Some copyright files aren't machine readable.
            return None

        # Obtain the main or overall license.
        if c.header.license:
            main_license = (c.header.license.synopsis,
                            self.find_license(c.header.license.synopsis))
        else:
            main_license = None

        # Obtain all the mentioned licenses.
        licenses = set()

        for para in c.all_license_paragraphs():
            try:
                licenses.add((para.license.synopsis,
                              self.find_license(para.license.synopsis)))

            except copyright.Error:
                # Some license paragraphs aren't machine readable.
                pass

        return main_license, licenses

    def find_license(self, name):

        name = name.rstrip('+')
        path = os.path.join(self.root_dir, 'usr', 'share', 'common-licenses', name)

        if os.path.exists(path):
            return os.path.join('/', 'usr', 'share', 'common-licenses', name)
        else:
            return None

    def list_packages(self):

        index_file = os.path.join(self.output_dir, 'index.html')
        index_text = ('<html>\n<head>\n<title>Installed Software</title>\n'
                      '<style type="text/css">\n'
                      'h1 { font-size: 1.6em; text-align: center; '
                      'border-bottom: solid 2px #e0e0e0; padding-bottom: 4px }\n'
                      '.version { font-size: 0.8em }\n'
                      'dt { font-size: 1.5em; font-weight: bold }\n'
                      'dd { border-bottom: solid 1px #c0c0c0; margin-bottom: 2px }\n'
                      '.links { font-size: 1.5em; margin-left: 0.5em; '
                      'margin-right: 0.5em; text-align: center }\n'
                      '.links a { font-family: monospace; margin-right: 0.2em }\n'
                      '</style>\n'
                      '</head>\n<body>\n')

        status_file = os.path.join(self.root_dir, 'var', 'lib', 'dpkg', 'status')
        deb_host = 'https://repo.pureos.net/pureos/'

        total = open(status_file).read().count('\n\n')

        index_text += '<h1>Package Information</h1>\n\n'
        current = None
        i = 0

        links_text = '<p class="links">'
        index_list_text = '<dl>\n'

        for package in deb822.Packages.iter_paragraphs(open(status_file)):

            if 'installed' not in package['Status']:
                continue

            name = package['Package']
            homepage = package.get('Homepage', '')
            version = package['Version']

            if name[0] != current:
                current = name[0]
                index_list_text += '<dt><a name="%s">%s</a></dt>\n' % (current, current)
                links_text += '<a href="#%s">%s</a> ' % (current, current)

            index_list_text += ('<dd><a href="%s.html">%s</a><br />\n'
                                '<span class="version">%s</span></dd>\n') % (
                                name, name, version)

            i += 1
            yield name, i, total

            output_text = ('<html>\n<head>\n<title>%s</title>\n'
                           '<style type="text/css">\n'
                           'th { text-align: left }\n'
                           'pre { font-size: 0.9em }\n'
                           '</style>\n'
                           '</head>\n<body>\n') % self.html_escape(name)

            output_text += '<h1>%s</h1>\n\n' % self.html_escape(name)

            # Read the description from the package information.
            description = package['Description']

            output_text += '<h2>Description</h2>\n'

            if description:

                output_text += '<pre>\n'

                for line in description.split('\n'):
                    line = line.lstrip()
                    if line == '.':
                        output_text += '\n'
                    else:
                        output_text += self.html_escape(line) + '\n'

                output_text += '</pre>\n'

            output_text += '<table>\n'
            output_text += '<tr><th>Version</th><td>%s</td></tr>\n' % self.html_escape(version)

            if homepage:
                output_text += '<tr><th>Home page</th><td><a href="%s">%s</a></td></tr>\n' % (
                    homepage, self.html_escape(homepage))

            # Use the package name to find the copyright file.
            copyright_file = self.get_copyright(name)

            if copyright_file:

                output_text += '<tr><th>Copyright</th><td><a href="file://%s">%s</a></td></tr>\n' % (
                    copyright_file, self.html_escape(copyright_file))

                licenses = self.get_licenses(copyright_file)

                if licenses:
                    main_license, licenses = licenses
                    if main_license:
                        output_text += '<tr><th>Overall License</th>\n<td>'
                        license_name, license_file = main_license
                        output_text += self.make_link(license_file, license_name)
                        output_text += '</td></tr>\n'

                    if licenses:
                        output_text += '<tr><th>Licenses</th>\n<td>'
                        for license_name, license_file in licenses:
                            output_text += self.make_link(license_file, license_name) + ' '

                        output_text += '</td></tr>\n'

            output_text += '</table>\n'

            # Read source files.
            info = self.get_source_files(name)

            if info:
                output_text += ('<h2>Source Files</h2>\n\n'
                                'These files can be downloaded to rebuild the package:\n\n')

                source, files = info
                output_text += '<ul>\n'

                for item in files:
                    output_text += '<li><a href="%s%s/%s">%s</a></li>\n' % (deb_host,
                        source['Directory'], item['name'], self.html_escape(item['name']))

                output_text += '</ul>\n'

            open(os.path.join(self.output_dir, name + '.html'), 'w').write(output_text)

        index_text += links_text + '\n</p>\n' + index_list_text + '</dl>\n'
        index_text += links_text + '</body>\n</html>\n'

        open(index_file, 'w').write(index_text)
