/*
	PluginFramework
	by John Ryland
	Copyright (c) 2023
*/

////////////////////////////////////////////////////////////////////////////////////
//	Plugin Loader

#include "PluginManager.h"
#include "Library.h"

#ifdef _WIN32
#  define WIN32_LEAN_AND_MEAN
#  include <windows.h>
#else //UNIX
#  include <dlfcn.h>
#endif

namespace PluginFramework {

typedef void (*RegisterPluginFuncPtr)(PluginManager& pluginManager);
typedef void (*DeregisterPluginFuncPtr)(PluginManager& pluginManager);

Plugin::Plugin(std::string pluginName, const char* libraryFileName)
    : mName(pluginName)
    , mManager(nullptr)
    , mLibrary(Library::Load(libraryFileName))
    , mRegisterFuncPtr(nullptr)
{
    Load();
}

Plugin::Plugin(Plugin&& other)
    : mName(std::move(other.mName))
    , mManager(std::move(other.mManager))
    , mLibrary(std::move(other.mLibrary))
    , mRegisterFuncPtr(std::move(other.mRegisterFuncPtr))
{
}

Plugin::~Plugin()
{
    Deregister();
}

void Plugin::Load()
{
    if (mLibrary.get() && mLibrary->Loaded())
    {
        mRegisterFuncPtr = mLibrary->GetFunctionAddress(STRINGIZE(PLUGIN_REGISTER_FUNCTION));
        if (mRegisterFuncPtr)
            printf("found RegisterPlugin function\n");
        else
            printf("didn't find RegisterPlugin function\n");
    }
    else
    {
        printf("plugin not loaded\n");
    }

    if (mLibrary.get() && mLibrary->Loaded() && !mRegisterFuncPtr)
        mLibrary->Unload();
}

void Plugin::Deregister()
{
    if (Registered())
    {
        void* deregisterFuncPtr = mLibrary->GetFunctionAddress(STRINGIZE(PLUGIN_DEREGISTER_FUNCTION));
        if (deregisterFuncPtr)
            ((DeregisterPluginFuncPtr)deregisterFuncPtr)(*mManager);
    }
}

void Plugin::HotEject()
{
    Deregister();
    if (Registered())
        mManager->UnloadPlugin(*this);
    if (mLibrary.get())
        mLibrary->Unload();
}

bool Plugin::Loaded() const
{
    return mLibrary.get() && mLibrary->Loaded() && mRegisterFuncPtr;
}

bool Plugin::Registered() const
{
    return Loaded() && mManager;
}

std::string Plugin::Name() const
{
    return mName;
}

std::string Plugin::FileName() const
{
    if (mLibrary.get())
        return mLibrary->FileName();
    return "";
}

void Plugin::Register(PluginManager& pluginManager)
{
    if (Loaded() && !Registered())
    {
        ((RegisterPluginFuncPtr)mRegisterFuncPtr)(pluginManager);
        mManager = &pluginManager;
    }
}

} // PluginFramework namespace
