#ifndef SINGLETON_H
#define SINGLETON_H

#include <QMutex>
#include <QScopedPointer>

/**
 * 使用方法:
 * 1. 定义类为单例 (类声明时使用宏 SINGLETON):
 *    class Config {
 *        SINGLETON(Config)
 *    public:
 *
 * 2. 在 cpp 文件中实现类的默认构造函数和析构函数 (已经在宏 SINGLETON 里被声明为 private 的了，所以必须实现)
 *
 * 3. 调用单例的函数，可以任意使用下面三种方式中的一种:
 *    3.1 推荐调用: Config::instance().method();
 *    3.2 原始调用: Singleton<Config>::getInstance().method();
 *    3.3 保存引用，方便多次调用:
 *        Config &config = Singleton<Config>::getInstance();
 *        config.method1();
 *        config.method2();
 *        config.method3();
 *
 * 注意:
 *     如果单例的类需要释放的资源和 Qt 底层的信号系统有关系，例如 QSettings 的数据没有保存，QSqlDatabase 的连接没有关闭等，
 *     需要在 main 函数返回前手动释放，否则有可能在程序退出时报系统底层的信号错误，因为 main 函数返回后 qApp 已经被回收，
 *     而资源的释放在 main 返回后，又和信号槽有关，所以就可能报错。
 *     推荐实现方式: 可以在单例类的构造函数中给 qApp 的 aboutToQuit 信号绑定一个槽函数，在里面处理善后工作、释放资源等。
 */
template <typename T>
class SingletonEx {
public:
    static T& getInstance(); // 获取单例的唯一实例对象

    SingletonEx(const SingletonEx &other) = delete;
    SingletonEx<T>& operator=(const SingletonEx &other) = delete;

private:
    static QMutex mutex;
    static QScopedPointer<T> instance;
};

/*-----------------------------------------------------------------------------|
 |                          Singleton Implementation                           |
 |----------------------------------------------------------------------------*/
template <typename T> QMutex SingletonEx<T>::mutex;
template <typename T> QScopedPointer<T> SingletonEx<T>::instance;

template <typename T>
T& SingletonEx<T>::getInstance() {
    if (instance.isNull()) {
        mutex.lock();
        if (instance.isNull()) {
            instance.reset(new T()); // 此指针会在全局变量作用域结束时自动 deleted (main 函数返回后)
        }
        mutex.unlock();
    }

    return *instance.data();
}

/*-----------------------------------------------------------------------------|
 |                               Singleton Macro                               |
 |----------------------------------------------------------------------------*/
#define SINGLETONEX(Class)                           \
private:                                           \
    Class();                                       \
    ~Class();                                      \
    Class(const Class &other) = delete;            \
    Class& operator=(const Class &other) = delete; \
    friend class  SingletonEx<Class>;                \
    friend struct QScopedPointerDeleter<Class>;    \
                                                   \
public:                                            \
    static Class& instance() {                     \
        return SingletonEx<Class>::getInstance();    \
    }

#endif // SINGLETON_H
