/*
 * Copyright (c) 2019 Frank Fischer <frank-fischer@shadow-soft.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 3 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, see  <http://www.gnu.org/licenses/>
 */

#ifndef __KOLLAGEIMAGECACHE_HXX__
#define __KOLLAGEIMAGECACHE_HXX__

#include <QtCore/QObject>

#include <memory>

class KollageImageData;

class KollageFutureImage : public QObject
{
    Q_OBJECT

    friend class KollageImageCache;

public:
    KollageFutureImage(std::shared_ptr<KollageImageData> data, QObject* parent = nullptr);

    KollageFutureImage(const KollageFutureImage& image, QObject* parent = nullptr);

    ~KollageFutureImage() override;

    /// Mark this image as sticky.
    ///
    /// A sticky image won't be removed from the cache.
    void stick();

    /// Unmark this image as sticky.
    ///
    /// The image might be removed from the cache.
    void unstick();

    /// Return true iff the image is in the cache.
    bool isReady() const;

    /// Return true iff loading the image has failed.
    bool isFailed() const;

    /// Return the size of the image if present in the cache.
    QSize size() const;

    /// Return the image if present in the cache.
    QImage image() const;

    /// Force the image to be fetched from the cache.
    ///
    /// If `fetch_failed` is `true`, the image is refetched even if
    /// the previous fetch failed.
    void fetch(bool fetch_failed);

    /// \overload
    void fetch() { fetch(false); }

    /// Return the source Url of this image.
    QUrl url() const;

signals:
    void ready();

    void failed(const QString& errorMessage);

private:
    std::shared_ptr<KollageImageData> data_;
};

class KollageImageCache : public QObject
{
    Q_OBJECT

    friend class KollageImageData;

public:
    KollageImageCache(QObject* parent = nullptr);

    ~KollageImageCache() override;

    void setMaxCache(int max_cache);

    int maxCache() const;

    std::unique_ptr<KollageFutureImage> get(const QUrl& url);

    static KollageImageCache& instance();

private:
    /// Store the given image under the given Url in the cache.
    void addImage(const QUrl& url, std::shared_ptr<QImage> image);

    /// Return the image stored under the given Url from the cache.
    ///
    /// The function returns `nullptr` iff the image is currently not in the
    /// cache.
    std::shared_ptr<QImage> getImage(const QUrl& url) const;

    /// The given image has just been in use.
    void useImage(const QUrl& url);

    /// Clean the cache from old images.
    void clean();

private slots:
    void removeImageData(QObject* data);

signals:
    void maxCacheChanged();

private:
    struct Data;
    std::unique_ptr<Data> d;
};

#endif
