/* This file is part of the Chakra project

   Copyright (C) 2010 Dario Freddi <drf@chakra-project.org>
   Copyright (C) 2011 Lukas Appelhans <l.appelhans@gmx.de>

   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.
*/

#ifndef AKABEIPACKAGE_H
#define AKABEIPACKAGE_H

#include <akabeicore_global.h>

#include <QObject>
#include <QSharedDataPointer>

class QDateTime;
class QUrl;
namespace Akabei {
class Delta;

class Group;
class Database;
class Hook;

class PackagePrivate;
class VersionData;
/**
 * \class Package  akabeipackage.h "akabeipackage.h"
 *
 * \brief This class describes a basic package which is usually loaded from a database.
 *
 * Although normal packages are loaded by the Database, one can use Backend::loadPackageFromFile when
 * such a thing does not exist.
 * When using a Package object, one has to take into consideration that this does not mean that a
 * package exists on disk. It merely means that there is an SQL row in a database which shares the information
 * which is represented in this object.
 * If the package exists on disk, pathToArchive() will point to a valid file.
 * @see pathToArchive()
 * @see database()
 *
 * This class is thread-safe.
 */
class AKABEICORESHARED_EXPORT Package
{
    Q_DISABLE_COPY(Package)
    Q_DECLARE_PRIVATE(Package)
public:
    enum Flag {
        NoFlags = 0,
        DebugFlag = 1,
        // More flags here.
    };

    class Version {
        public:
            Version(const Akabei::Package::Version &other);
            Version();

            explicit Version(const QByteArray &version, unsigned int epoch = 0);
            virtual ~Version();

            bool isValid() const;

            QByteArray toByteArray() const;
            unsigned int epoch() const;
            bool respectsConstraint(const QString &constraintedVersion) const;
            static bool respectsConstraint(const QByteArray &version, const QString &constraintedVersion);
            static bool respectsConstraint(const QString &constraintedVersion, const QString &constraintedVersion2);

            bool operator==(const Version &other) const;
            bool operator!=(const Version &other) const;
            bool operator<(const Version &other) const;
            bool operator<=(const Version &other) const;
            bool operator>(const Version &other) const;
            bool operator>=(const Version &other) const;

            Version &operator=(const QByteArray &other);
            bool operator==(const QByteArray &other) const;
            bool operator!=(const QByteArray &other) const;
            bool operator<(const QByteArray &other) const;
            bool operator<=(const QByteArray &other) const;
            bool operator>(const QByteArray &other) const;
            bool operator>=(const QByteArray &other) const;

        private:
            QSharedDataPointer<VersionData> d;

            friend class PackagePrivate;
    };

    /**
     * This enum describes the reason why a package got installed.
     */
    enum InstallReason {
        /**
         * NoReason is just a placeholder, e.g. when a package is not installed at all
         */
        NoReason = 0,
        /**
         * The package got explicitly installed, aka the user selected it
         */
        ExplicitlyInstalledReason = 1,
        /**
         * The package got installed as a dependency
         */
        InstalledAsDependencyReason = 2
    };

    enum FilepathMode {
        FilepathNoPrefix = 0,
        FilepathOnDisk = 1,
        FilepathIntelligent = 2
    };

    virtual ~Package();

    /**
     * @returns the database id of the package. Every package has a unique ID in a database. Packages from different
     * databases can possibly have the same database id.
     */
    int databaseId() const;

    typedef QList<Package*> List;

    /**
     * @returns the name of the package
     */
    QString name() const;
    /**
     * @returns the filename of the package on disk
     */
    QString filename() const;
    /**
     * @returns the version of the package
     */
    Version version() const;
    /**
     * @returns the description of the package
     */
    QString description() const;
    /**
     * @returns the url to a website about the package/the program shipped
     */
    QUrl url() const;
    /**
     * @returns the packager of the package
     */
    QString packager() const;
    /**
     * @returns the md5sum of the package archive
     */
    QByteArray md5sum() const;
    /**
     * @returns the arch the package is built for
     */
    QString arch() const;
    /**
     * @returns the build date of the package
     */
    QDateTime buildDate() const;
    /**
     * @returns the install date of the package
     */
    QDateTime installDate() const;
    /**
     * @returns the size of the package
     */
    qint32 size() const;
    /**
     * @returns the installed size of the package
     */
    qint32 installedSize() const;
    /**
     * @returns a screenshot to the application inside the package
     */
    QUrl screenshot() const;
    /**
     * @returns why the package got installed
     */
    InstallReason installReason() const;
    /**
     * @returns the license of the package
     */
    QStringList licenses() const;
    /**
     * @returns a list of package names which the current package replaces
     */
    QStringList replaces() const;
    /**
     * @returns a list of package names which are optional dependencies
     */
    QStringList optionalDependencies() const;
    /**
     * @returns true when the package owns a scriptlet which needs to be executed on a transaction
     */
    bool hasScriptlet() const;
    /**
     * @returns true when the package needs to trigger a hook on a transaction
     */
    bool hasHooks() const;

    /**
     * @returns a list of files the package contains, this will read the files from database, so when
     * first called it's expensive
     */
    QStringList retrieveFiles(FilepathMode mode = FilepathIntelligent);
    /**
     * @returns a map of files which is going to be backupped when there are newer versions available
     * and the current version changed on disk
     * The key represents the path on disk, the value is the md5sum of the backed up file.
     */
    QMap<QString, QString> backupFiles() const;
    
    /**
     * @returns a list of files which the package ultimately owns (it forces the ownership on itself)
     */
    QStringList ultimatelyOwnedFiles() const;
    
    /**
     * @returns a list of hook-names the package needs to trigger on a transaction
     */
    QStringList hooks();
    /**
     * @returns a list of hooks the package needs to trigger on a transaction
     * This method is expensive on first call, because it needs to create the hook objects from the database.
     */
    QList<Hook*> retrieveHooks();
    /**
     * @returns the scriptlet of the package
     */
    QString retrieveScriptlet();

    /**
     * @returns a list of dependencies for the package
     * This method is expensive on first call, because it needs to resolve a list of package names into a list of packages.
     * @see dependencies()
     */
    List computeDependencies();

    /**
     * @returns a list of packages which have the current package as dependency
     * This method is expensive on first call, because it needs to resolve a list of package names to a list of packages.
     */
    List computeRequiredBy();
    /**
     * @returns a list of packages which have the current package as dependency and are installed
     * This method is expensive on first call, because it needs to resolve a list of package names to a list of packages.
     */
    List computeRequiredByInstalled();
    /**
     * @returns a list of groups the package is in
     */
    QList<Group*> groups() const;

    /**
     * @returns a list of package names which the package provides
     */
    QStringList provides() const;
    /**
     * @returns a list of package names the package depends on
     * @see computeDependencies
     */
    QStringList dependencies() const;
    /**
     * @returns a list of package names that are make dependencies of this one.
     */
    QStringList makeDependencies() const;
    /**
     * @returns a list of package names which conflict with the current package
     */
    QStringList conflictsWith() const;

    /**
     * @returns the database which contains the current package
     */
    Database *database() const;
    /**
     * @returns true if the package is valid
     */
    bool isValid() const;
    /**
     * @returns true if the package if this package, any greater version or provider is installed
     */
    bool isInstalled() const;

    /**
     * @returns the changelog of the package
     */
    QString retrieveChangelog() const;
    /**
     * @returns the logged actions of the package
     */
    QString retrieveLoggedActions() const;

    /**
     * @returns a list of mimetypes the package is able to execute
     */
    QStringList mimetypes() const;

    /**
     * @returns the path to the package archive on disk
     */
    QString pathToArchive() const;
    /**
     * Sets the path to the package archive on disk
     * @param path the archive path
     */
    void setPathToArchive(const QString &path);

    /**
     * @returns true if the md5sum of the package and the md5sum of the archive on disk match
     */
    bool checkMD5Sum();
    /**
     * @returns true if the archive on disk is valid
     */
    bool checkArchiveValidity();

    /**
     * @returns the delta to version @param version
     */
    Delta* retrieveDeltaTo(const Version &version = Version());
    /**
     * @returns the delta from version @param version
     */
    Delta *retrieveDeltaFrom(const Version &version = Version());
    /**
     * @returns a list of deltas which exist for this package
     */
    QList< Delta* > retrieveDeltas();

    /**
     * Creates a new package which can then be inserted into the local database
     * @param reason the reason why the package got installed
     * @param installDate the current QDateTime the package got installed
     * @returns a new package object which has the same properties than the current package,
     * but with the additional parameters set
     */
    Package *generateInstalledPackage(InstallReason reason, const QDateTime &installDate);

    /**
     * @returns true if the current package is lower than the other package
     */
    bool operator<(const Package &other) const;

    /**
     * @returns true if the current package is the same than the other package
     */
    bool operator==(const Package &other) const;


    /**
     * @returns the git repository where we store the PKGBUILD
     */
    QString gitRepo() const;

    /**
     * @returns the git branch where we store the PKGBUILD
     */
    QString gitBranch() const;

    /**
     * @returns the git folder where we store the PKGBUILD
     */
    QString gitFolder() const;

private:
    PackagePrivate * const d_ptr;

    Package(Database *db, int databaseId, const QString &name);
    Package();

    friend class Backend;
    friend class Database;
    friend class DatabasePrivate;
    friend class QueryHelper;
};

}

#endif // AKABEIPACKAGE_H
