# vim:set fileencoding=utf-8 et ts=4 sts=4 sw=4:
#
#   apt-listchanges - Show changelog entries between the installed versions
#                     of a set of packages and the versions contained in
#                     corresponding .deb files
#
#   Copyright (C) 2000-2006  Matt Zimmerman  <mdz@debian.org>
#   Copyright (C) 2006       Pierre Habouzit <madcoder@debian.org>
#   Copyright (C) 2016       Robert Luberda  <robert@debian.org>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   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 St, Fifth Floor, Boston, MA 02110-1301, USA.
#

from dbm import ndbm
from ALChacks import _
import os
import shutil


class DbError(Exception):
    pass

class seendb_dummy(object):
    '''Interface for seen database.
       Also used when path to the database is not configured'''
    def __contains__(self, srcpackage):
        return False

    def __getitem__(self, srcpackage):
        pass

    def __setitem__(self, srcpackage, version):
        pass
	
    def close_db(self):
        pass

    def apply_changes(self):
        pass

    def dump(self):
        raise DbError(_("Path to the seen database is unknown.\n"
                        "Please either specify it with --save-seen option\n"
                        "or pass --profile=apt to have it read from the configuration file."))

class seendb(seendb_dummy):
    '''Class to manage the seen database'''
    def __init__(self, path, readOnly = False):
        super().__init__()

        self._extension = '.db'
        if path[-3:] != self._extension:
            raise DbError(_("Database %(db)s does not end with %(ext)s")
                            % {'db': path, 'ext': self._extension})

        self._dbpath   = path[:-3] # strip the .db suffix
        try:
            mode = 'r' if readOnly else 'c'
            self._seen = ndbm.open(self._dbpath, mode, 0o644)
            'foo%0' in self._seen
        except Exception as ex:
            raise DbError(_("Database %(db)s failed to load: %(errmsg)s")
                            % {'db': path, 'errmsg': str(ex)}) from ex

        # Will replace seen after changes have actually been seen
        self._seen_new = {}

    def __contains__(self, srcpackage):
        return srcpackage in self._seen

    def __getitem__(self, srcpackage):
        return self._seen[srcpackage].decode()

    def __setitem__(self, srcpackage, version):
        if self._seen.get(srcpackage, b'').decode() != version:
            # Note: updating _seen_new, not _seen
            self._seen_new[srcpackage] = version
	
    def close_db(self):
        self._seen.close()
        self._seen = None

    def apply_changes(self):
        if not self._seen_new:
            # Nothing to update
            return

        def mk(arg):
            return self._dbpath + arg + self._extension
        old, cur, new = (mk('-old'), mk(''), mk('-new'))

        # For reliability and to have backup of the database,
        # it is updated in the following steps:
        # 1. copy current version to new
        if os.path.isfile(cur):
            shutil.copy(cur, new)

        # 2. apply the changes to new
        seen = ndbm.open(self._dbpath + '-new', 'c', 0o644)
        for (key, value) in self._seen_new.items():
            seen[key] = value
        seen.close()

        # 3. save current as old, and rename new to current
        if os.path.isfile(old):
            os.unlink(old)
        if os.path.isfile(cur):
            os.link(cur, old)
        os.rename(new, cur)

    def dump(self):
        for key in sorted(self._seen.keys()):
            value = self._seen[key]
            print("%s %s" % (key.decode(), value.decode()))


def make_seen_db(config, readOnly = False):
    if config.save_seen:
        return seendb(config.save_seen, readOnly)
    return seendb_dummy()
