//
//  User or remote site invokes applet
//

#include "precomp.h"
#include "appldr.h"
#include "cuserdta.hpp"
#include "csap.h"

#define count_of(array)		(sizeof(array) / sizeof(array[0]))


static  CRITICAL_SECTION  g_csAppLdrInfo;
static  AppLoaderInfo  g_aAppLoaderInfo[APPLET_LAST];
static  BOOL   g_fAppLdrInitialized = FALSE;

const static CHAR *g_fnAppletDLL[APPLET_LAST] = {"nmwb.dll", "nmft.dll", "nmchat.dll"};

// Chat session key
static const GUID guidNM2Chat = { 0x340f3a60, 0x7067, 0x11d0,
						 { 0xa0, 0x41, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };
#define  CHAT_KEY_SIZE  25
extern struct Key CHAT_APP_PROTO_KEY;


// NetMeeting/UI,  T.120
T120Error  WINAPI  T120_LoadApplet
(
    APPLET_ID       nAppId,
    BOOL            flocal,
    T120ConfID      nConfId,
    BOOL            fNoUI,
    LPSTR           pszCmdLine
)
{
	T120Error   rc = T120_NO_ERROR;
	LPFN_CREATE_APPLET_LOADER_INTERFACE		pfnCreateInterface;

	if (nAppId >= APPLET_LAST)
	{
		ERROR_OUT(("T120_LoadApplet: invalid applet ID=%u", nAppId));
		return T120_INVALID_PARAMETER;
	}

    //
    // Check policies.  Launch & auto-launch not allowed if corp has
    // disabled applet.
    //
    RegEntry    rePol(POLICIES_KEY, HKEY_CURRENT_USER);
    switch (nAppId)
    {
        case APPLET_ID_WB:
            if (rePol.GetNumber(REGVAL_POL_NO_NEWWHITEBOARD, DEFAULT_POL_NO_NEWWHITEBOARD))
            {
                WARNING_OUT(("New WB disabled by policy, not starting"));
	            return GCC_NO_SUCH_APPLICATION;
            }
            break;

        case APPLET_ID_FT:
            if (rePol.GetNumber(REGVAL_POL_NO_FILETRANSFER_SEND, DEFAULT_POL_NO_FILETRANSFER_SEND) &&
                rePol.GetNumber(REGVAL_POL_NO_FILETRANSFER_RECEIVE, DEFAULT_POL_NO_FILETRANSFER_RECEIVE))
            {
                WARNING_OUT(("FT disabled by policy, not starting"));
                return GCC_NO_SUCH_APPLICATION;
            }
            break;

        case APPLET_ID_CHAT:
            if (rePol.GetNumber(REGVAL_POL_NO_CHAT, DEFAULT_POL_NO_CHAT))
            {
                WARNING_OUT(("Chat disabled by policy, not starting"));
                return GCC_NO_SUCH_APPLICATION;
            }
            break;
    }

	::EnterCriticalSection(&g_csAppLdrInfo);

	if (NULL != g_aAppLoaderInfo[nAppId].hLibApplet)
	{
		switch (g_aAppLoaderInfo[nAppId].eStatus)
		{
		case APPLET_CLOSING:
		case APPLET_WORK_THREAD_EXITED:
			WARNING_OUT(("T120_LoadApplet: applet is closing or work thread exited"));
			rc = GCC_APPLET_EXITING;
			break;
		default:
			if (APPLDR_NO_ERROR == g_aAppLoaderInfo[nAppId].pIAppLoader->AppletInvoke(flocal, nConfId, pszCmdLine))
			{
			    g_aAppLoaderInfo[nAppId].cLoads++;
			}
			// rc = T120_NO_ERROR;
			break;
		}
		goto MyExit;
	}

	g_aAppLoaderInfo[nAppId].hLibApplet = ::LoadLibrary(g_fnAppletDLL[nAppId]);
	if (NULL != g_aAppLoaderInfo[nAppId].hLibApplet)
	{
		pfnCreateInterface = (LPFN_CREATE_APPLET_LOADER_INTERFACE)
					::GetProcAddress(g_aAppLoaderInfo[nAppId].hLibApplet,
									CREATE_APPLET_LOADER_INTERFACE);
		if (NULL != pfnCreateInterface)
		{
			//g_aAppLoaderInfo[nAppId].pIAppLoader = (IAppletLoader*)(*pfnCreateInterface)();
			(*pfnCreateInterface)(&g_aAppLoaderInfo[nAppId].pIAppLoader);
			if (NULL != g_aAppLoaderInfo[nAppId].pIAppLoader)
			{
				if (APPLDR_NO_ERROR == g_aAppLoaderInfo[nAppId].pIAppLoader->AppletStartup(fNoUI))
				{
					if (APPLDR_NO_ERROR == g_aAppLoaderInfo[nAppId].pIAppLoader->AppletInvoke(flocal, nConfId, pszCmdLine))
					{
						g_aAppLoaderInfo[nAppId].cLoads++;
						// rc = T120_NO_ERROR;
						goto MyExit;
					}
					else
					{
						ERROR_OUT(("T120_LoadApplet: cannot invoke applet(%s), flocal=%u, nConfID=%u",
							g_fnAppletDLL[nAppId], flocal, nConfId));
					}
				}
				else
				{
					ERROR_OUT(("T120_LoadApplet: cannot start applet(%s)", g_fnAppletDLL[nAppId]));
				}

				g_aAppLoaderInfo[nAppId].pIAppLoader->ReleaseInterface();
				g_aAppLoaderInfo[nAppId].pIAppLoader = NULL;
			}
			else
			{
				ERROR_OUT(("T120_LoadApplet: Entry function of %s  failed.\n", g_fnAppletDLL[nAppId]));
			}
		}
		else
		{
			WARNING_OUT(("T120_LoadApplet: Can't find entry point of %s.\n", g_fnAppletDLL[nAppId]));
		}

		::FreeLibrary(g_aAppLoaderInfo[nAppId].hLibApplet);
		g_aAppLoaderInfo[nAppId].hLibApplet = NULL;
	}
	else
	{
		ERROR_OUT(("T120_LoadApplet: Can't open DLL %s,  err %d.\n", g_fnAppletDLL[nAppId], GetLastError()));
	}

	rc = GCC_NO_SUCH_APPLICATION;
	
MyExit:

	::LeaveCriticalSection(&g_csAppLdrInfo);
	return rc;
}


//  NetMeeting/UI shutdown
T120Error WINAPI 
T120_CloseApplet(APPLET_ID  nAppId, BOOL fNowRegardlessRefCount, BOOL fSync, DWORD dwTimeout)
{
	if (nAppId< APPLET_LAST)
	{
		if (g_fAppLdrInitialized)
		{
			::EnterCriticalSection(&g_csAppLdrInfo);
			IAppletLoader *pIAppLdr = g_aAppLoaderInfo[nAppId].pIAppLoader;
			if (NULL != pIAppLdr)
			{
				ASSERT(g_aAppLoaderInfo[nAppId].cLoads > 0);
				g_aAppLoaderInfo[nAppId].cLoads --;
				if ((! fNowRegardlessRefCount) && g_aAppLoaderInfo[nAppId].cLoads > 0)
				{
					pIAppLdr = NULL; // do not free the library
				}
			}
			::LeaveCriticalSection(&g_csAppLdrInfo);

			if (NULL != pIAppLdr)
			{
                // AppletCleanup() must be outside of the critical section
                // because applet worker thread will call AppletStatus() before
                // exiting its worker thread.
                switch (pIAppLdr->AppletCleanup(5000)) // always synchronous shutdown
                {
                case APPLDR_NO_ERROR :
                    // we are closing this applet
					g_aAppLoaderInfo[nAppId].eStatus = APPLET_CLOSING;

                    // it is safe to unload the library
   					::FreeLibrary(g_aAppLoaderInfo[nAppId].hLibApplet);
   					g_aAppLoaderInfo[nAppId].hLibApplet = NULL;
					break;

			    case APPLDR_CANCEL_EXIT:
					//
					// The app didn't want to be unloaded
					//
					::EnterCriticalSection(&g_csAppLdrInfo);
					g_aAppLoaderInfo[nAppId].cLoads++;
					g_aAppLoaderInfo[nAppId].pIAppLoader = pIAppLdr;
					::LeaveCriticalSection(&g_csAppLdrInfo);
    				return GCC_APPLET_CANCEL_EXIT;

			    default:
			        break;
				}
			}
		}
		return T120_NO_ERROR;
	}
	else
	{
		ERROR_OUT(("T120_CloseApplet: invalid applet ID=%u", nAppId));
	}
	return T120_INVALID_PARAMETER;
}


T120Error WINAPI 
T120_QueryApplet(APPLET_ID  nAppId, APPLET_QUERY_ID eQueryId)
{
	if (nAppId< APPLET_LAST)
	{
		if (g_fAppLdrInitialized)
		{
			::EnterCriticalSection(&g_csAppLdrInfo);
			IAppletLoader *pIAppLdr = g_aAppLoaderInfo[nAppId].pIAppLoader;
			::LeaveCriticalSection(&g_csAppLdrInfo);
			if (NULL != pIAppLdr)
			{
				if (APPLET_QUERY_NM2xNODE == eQueryId)
				{
					pIAppLdr->OnNM2xNodeJoin();
				}
				else if (APPLDR_CANCEL_EXIT == pIAppLdr->AppletQuery(eQueryId))
				{
                    return GCC_APPLET_CANCEL_EXIT;
                }
			}
		}
	    return		T120_NO_ERROR;
	}
	else
	{
		ERROR_OUT(("T120_CloseApplet: invalid applet ID=%u", nAppId));
	}
	return T120_INVALID_PARAMETER;
}


// Applet itself
T120Error WINAPI 
T120_AppletStatus(APPLET_ID  nAppId, APPLET_STATUS  status)
{
	if (nAppId < APPLET_LAST)
	{
		if (g_fAppLdrInitialized)
		{
			::EnterCriticalSection(&g_csAppLdrInfo);

			g_aAppLoaderInfo[nAppId].eStatus = status;

			switch (status)
			{
			case APPLET_WORK_THREAD_EXITED:
				if (NULL != g_aAppLoaderInfo[nAppId].pIAppLoader)
				{
					g_aAppLoaderInfo[nAppId].pIAppLoader->ReleaseInterface();
					g_aAppLoaderInfo[nAppId].pIAppLoader = NULL;
				}
				break;
			case APPLET_LIBRARY_FREED:
				// clean up this entry
				::ZeroMemory(&g_aAppLoaderInfo[nAppId], sizeof(g_aAppLoaderInfo[0]));
				break;
			}

			::LeaveCriticalSection(&g_csAppLdrInfo);
		}
		return T120_NO_ERROR;
	}
	else
	{
		ERROR_OUT(("T120_AppletStatus: invalid applet ID=%u", nAppId));
	}
	return T120_INVALID_PARAMETER;
}


T120Error AppLdr_Initialize(void)
{ 
	ASSERT(count_of(g_aAppLoaderInfo) == APPLET_LAST);

	::InitializeCriticalSection(&g_csAppLdrInfo);
	
	// clean all entries
	::ZeroMemory(g_aAppLoaderInfo, sizeof(g_aAppLoaderInfo));

	::CreateH221AppKeyFromGuid(CHAT_APP_PROTO_KEY.u.h221_non_standard.value,
								(GUID *)&guidNM2Chat );
	CHAT_APP_PROTO_KEY.choice = h221_non_standard_chosen;
	CHAT_APP_PROTO_KEY.u.h221_non_standard.length = CHAT_KEY_SIZE;

	g_fAppLdrInitialized = TRUE;
	return T120_NO_ERROR;
}


void AppLdr_Shutdown(void)
{
	g_fAppLdrInitialized = FALSE;

	for (ULONG i = 0; i < APPLET_LAST; i++)
	{
		if (NULL != g_aAppLoaderInfo[i].pIAppLoader)
		{
			APPLDR_RESULT rc = g_aAppLoaderInfo[i].pIAppLoader->AppletCleanup(5000); // always synchronous shutdown
			ASSERT(APPLDR_NO_ERROR == rc);

            // it is safe to unload the library
            ::FreeLibrary(g_aAppLoaderInfo[i].hLibApplet);
            g_aAppLoaderInfo[i].hLibApplet = NULL;
		}
	}

	// clean all entries
	::ZeroMemory(g_aAppLoaderInfo, sizeof(g_aAppLoaderInfo));

	::DeleteCriticalSection(&g_csAppLdrInfo);
}