/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
#include "cmDynamicLoader.h"

#include <map>
#include <string>
#include <utility>

class cmDynamicLoaderCache
{
public:
    ~cmDynamicLoaderCache();
    void CacheFile(const char* path, cmsys::DynamicLoader::LibraryHandle /*p*/);
    bool GetCacheFile(const char* path,
                      cmsys::DynamicLoader::LibraryHandle& /*p*/);
    bool FlushCache(const char* path);
    void FlushCache();
    static cmDynamicLoaderCache* GetInstance();

private:
    std::map<std::string, cmsys::DynamicLoader::LibraryHandle> CacheMap;
    static cmDynamicLoaderCache*                               Instance;
};

cmDynamicLoaderCache* cmDynamicLoaderCache::Instance = nullptr;

cmDynamicLoaderCache::~cmDynamicLoaderCache() {}

void
cmDynamicLoaderCache::CacheFile(const char*                         path,
                                cmsys::DynamicLoader::LibraryHandle p)
{
    cmsys::DynamicLoader::LibraryHandle h;
    if(this->GetCacheFile(path, h))
    {
        this->FlushCache(path);
    }
    this->CacheMap[path] = p;
}

bool
cmDynamicLoaderCache::GetCacheFile(const char*                          path,
                                   cmsys::DynamicLoader::LibraryHandle& p)
{
    std::map<std::string, cmsys::DynamicLoader::LibraryHandle>::iterator it =
        this->CacheMap.find(path);
    if(it != this->CacheMap.end())
    {
        p = it->second;
        return true;
    }
    return false;
}

bool
cmDynamicLoaderCache::FlushCache(const char* path)
{
    std::map<std::string, cmsys::DynamicLoader::LibraryHandle>::iterator it =
        this->CacheMap.find(path);
    bool ret = false;
    if(it != this->CacheMap.end())
    {
        cmsys::DynamicLoader::CloseLibrary(it->second);
        this->CacheMap.erase(it);
        ret = true;
    }
    return ret;
}

void
cmDynamicLoaderCache::FlushCache()
{
    for(auto const& it : this->CacheMap)
    {
        cmsys::DynamicLoader::CloseLibrary(it.second);
    }
    delete cmDynamicLoaderCache::Instance;
    cmDynamicLoaderCache::Instance = nullptr;
}

cmDynamicLoaderCache*
cmDynamicLoaderCache::GetInstance()
{
    if(!cmDynamicLoaderCache::Instance)
    {
        cmDynamicLoaderCache::Instance = new cmDynamicLoaderCache;
    }
    return cmDynamicLoaderCache::Instance;
}

cmsys::DynamicLoader::LibraryHandle
cmDynamicLoader::OpenLibrary(const char* libname)
{
    cmsys::DynamicLoader::LibraryHandle lh;
    if(cmDynamicLoaderCache::GetInstance()->GetCacheFile(libname, lh))
    {
        return lh;
    }
    lh = cmsys::DynamicLoader::OpenLibrary(libname);
    cmDynamicLoaderCache::GetInstance()->CacheFile(libname, lh);
    return lh;
}

void
cmDynamicLoader::FlushCache()
{
    cmDynamicLoaderCache::GetInstance()->FlushCache();
}
