/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#include "stdafx.h"

// Plugin types supported
enum PluginInstanceType
{
	itScript,
	itControl
};

// Data associated with a plugin instance
struct PluginInstanceData {
	PluginInstanceType nType;
	union
	{
		CActiveScriptSiteInstance *pScriptSite;
		CControlSiteInstance *pControlSite;
	};
};


// NPP_Initialize
//
//	Initialize the plugin library. Your DLL global initialization
//	should be here
//
NPError NPP_Initialize(void)
{
	NG_TRACE_METHOD(NPP_Initialize);
	_Module.Lock();
    return NPERR_NO_ERROR;
}


// NPP_Shutdown
//
//	shutdown the plugin library. Revert initializition
//
void NPP_Shutdown(void)
{
	NG_TRACE_METHOD(NPP_Shutdown);
	_Module.Unlock();
}


// npp_GetJavaClass
//
//	Return the Java class representing this plugin
//
jref NPP_GetJavaClass(void)
{
	NG_TRACE_METHOD(NPP_GetJavaClass);

	//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
	// get the Java environment. You need this information pretty much for
	// any jri (Java Runtime Interface) call. 
//	JRIEnv* env = NPN_GetJavaEnv();
	//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
	// init any classes that define native methods, or whose methods we
	// are going to use.
	// The following functions are generated by javah running on the java
	// class(es) representing this plugin. javah generates the files
	// <java class name>.h and <java class name>.c (same for any additional
	// class you may want to use)
	// Return the main java class representing this plugin (derives from
	// Plugin class on the java side)
//	use_netscape_plugin_Plugin(env);
//	use_AviObserver(env);
//	return (jref)use_AviPlayer(env);
	// if no java is used
	return NULL;
}


#define MIME_OLEOBJECT1   "application/x-oleobject"
#define MIME_OLEOBJECT2   "application/oleobject"
#define MIME_ACTIVESCRIPT "text/x-activescript"


NPError NewScript(const char *pluginType,
					PluginInstanceData *pData,
					uint16 mode,
					int16 argc,
					char *argn[],
					char *argv[])
{
	CActiveScriptSiteInstance *pScriptSite = NULL;
	CActiveScriptSiteInstance::CreateInstance(&pScriptSite);

	// TODO support ActiveScript
	MessageBox(NULL, _T("ActiveScript not supported yet!"), NULL, MB_OK);
	return NPERR_GENERIC_ERROR;
}

NPError NewControl(const char *pluginType,
					PluginInstanceData *pData,
					uint16 mode,
					int16 argc,
					char *argn[],
					char *argv[])
{
	// Read the parameters
	CLSID clsid = CLSID_NULL;
	tstring szName;
	tstring szCodebase;
	PropertyList pl;

	for (int16 i = 0; i < argc; i++)
	{
		if (stricmp(argn[i], "CLSID") == 0 ||
			stricmp(argn[i], "CLASSID") == 0)
		{
			// Accept CLSIDs specified in various ways
			// e.g:
			//   "CLSID:C16DF970-D1BA-11d2-A252-000000000000"
			//   "C16DF970-D1BA-11d2-A252-000000000000"
			//   "{C16DF970-D1BA-11d2-A252-000000000000}"
			//
			// The first example is the proper way

			char szCLSID[256];
			if (strnicmp(argv[i], "CLSID:", 6) == 0)
			{
				char szTmp[256];
				sscanf(argv[i], "CLSID:%s", szTmp);
				sprintf(szCLSID, "{%s}", szTmp);
			}
			else if(argv[i][0] != '{')
			{
				sprintf(szCLSID, "{%s}", argv[i]);
			}
			else
			{
				strncpy(szCLSID, argv[i], sizeof(szCLSID));
			}

			USES_CONVERSION;
			CLSIDFromString(A2OLE(szCLSID), &clsid);
		}
		else if (stricmp(argn[i], "NAME") == 0)
		{
			USES_CONVERSION;
			szName = tstring(A2T(argv[i]));
		}
		else if (stricmp(argn[i], "CODEBASE") == 0)
		{
			szCodebase = tstring(A2T(argv[i]));
		}
		else if (strnicmp(argn[i], "PARAM_", 6) == 0)
		{
			USES_CONVERSION;

			std::wstring szName(A2W(argn[i] + 6));
			std::wstring szParam(A2W(argv[i]));
	
			// Empty parameters are ignored
			if (szName.empty())
			{
				continue;
			}

			// Check for existing params with the same name
			BOOL bFound = FALSE;
			for (PropertyList::const_iterator i = pl.begin(); i != pl.end(); i++)
			{
				if (wcscmp((BSTR) (*i).szName, szName.c_str()) == 0)
				{
					bFound = TRUE;
					break;
				}
			}
			// If the parameter already exists, don't add it to the
			// list again.
			if (bFound)
			{
				continue;
			}

			CComVariant vsValue(szParam.c_str());
			CComVariant vIValue; // Value converted to int
			CComVariant vRValue; // Value converted to real
			CComVariant &vValue = vsValue;

			// See if the variant can be converted to an integer
			if (VariantChangeType(&vIValue, &vsValue, 0, VT_I4) == S_OK)
			{
				vValue = vIValue;
			}
			else if (VariantChangeType(&vRValue, &vsValue, 0, VT_R8) == S_OK)
			{
				vValue = vRValue;
			}

			// Add named parameter to list
			Property p;
			p.szName = szName.c_str();
			p.vValue = vValue;
			pl.push_back(p);
		}
	}


	// Create the control site
	CControlSiteInstance *pSite = NULL;
	CControlSiteInstance::CreateInstance(&pSite);
	if (pSite == NULL)
	{
		return NPERR_GENERIC_ERROR;
	}
	pSite->AddRef();

	// TODO check the object is installed and at least as recent as
	//      that specified in szCodebase

	// Create the object
	if (FAILED(pSite->Create(clsid, pl, szName)))
	{
		USES_CONVERSION;
		LPOLESTR szClsid;
		StringFromCLSID(clsid, &szClsid);
		TCHAR szBuffer[256];
		_stprintf(szBuffer, _T("Could not create the control %s. Check that it has been installed on your computer and that this page correctly references it."), OLE2T(szClsid));
		MessageBox(NULL, szBuffer, _T("ActiveX Error"), MB_OK | MB_ICONWARNING);
		CoTaskMemFree(szClsid);

		pSite->Release();
		return NPERR_GENERIC_ERROR;
	}

	pData->nType = itControl;
	pData->pControlSite = pSite;

	return NPERR_NO_ERROR;
}


// NPP_New
//
//	create a new plugin instance 
//	handle any instance specific code initialization here
//
NPError NP_LOADDS NPP_New(NPMIMEType pluginType,
                NPP instance,
                uint16 mode,
                int16 argc,
                char* argn[],
                char* argv[],
                NPSavedData* saved)
{
	NG_TRACE_METHOD(NPP_New);

	// trap duff args
	if (instance == NULL)
	{
		return NPERR_INVALID_INSTANCE_ERROR;
	}

	PluginInstanceData *pData = new PluginInstanceData;
	if (pData == NULL)
	{
		return NPERR_GENERIC_ERROR;
	}

	// Create a plugin according to the mime type

	NPError rv = NPERR_GENERIC_ERROR;
	if (strcmp(pluginType, MIME_ACTIVESCRIPT) == 0)
	{
		rv = NewScript(pluginType, pData, mode, argc, argn, argv);
	}
	else if (strcmp(pluginType, MIME_OLEOBJECT1) == 0 ||
             strcmp(pluginType, MIME_OLEOBJECT2) == 0)
	{
		rv = NewControl(pluginType, pData, mode, argc, argn, argv);
	}
	else
	{
		// Unknown MIME type
	}

	// Test if plugin creation has succeeded and cleanup if it hasn't
	if (rv != NPERR_NO_ERROR)
	{
		delete pData;
		return rv;
	}

    instance->pdata = pData;

	return NPERR_NO_ERROR;
}


// NPP_Destroy
//
//	Deletes a plug-in instance and releases all of its resources.
//
NPError NP_LOADDS
NPP_Destroy(NPP instance, NPSavedData** save)
{
	NG_TRACE_METHOD(NPP_Destroy);

	PluginInstanceData *pData = (PluginInstanceData *) instance->pdata;
	if (pData == NULL)
	{
		return NPERR_INVALID_INSTANCE_ERROR;
	}

	if (pData->nType == itControl)
	{
		// Destroy the site
		CControlSiteInstance *pSite = pData->pControlSite;
		if (pSite)
		{
			pSite->Detach();
			pSite->Release();
		}
	}
	else if (pData->nType == itScript)
	{
		// TODO
	}

	delete pData;

	instance->pdata = 0;

    return NPERR_NO_ERROR;

}


// NPP_SetWindow
//
//	Associates a platform specific window handle with a plug-in instance.
//		Called multiple times while, e.g., scrolling.  Can be called for three
//		reasons:
//
//			1.  A new window has been created
//			2.  A window has been moved or resized
//			3.  A window has been destroyed
//
//	There is also the degenerate case;  that it was called spuriously, and
//  the window handle and or coords may have or have not changed, or
//  the window handle and or coords may be ZERO.  State information
//  must be maintained by the plug-in to correctly handle the degenerate
//  case.
//
NPError NP_LOADDS
NPP_SetWindow(NPP instance, NPWindow* window)
{
	NG_TRACE_METHOD(NPP_SetWindow);

	// Reject silly parameters
	if (!window)
	{
		return NPERR_GENERIC_ERROR;
	}

	PluginInstanceData *pData = (PluginInstanceData *) instance->pdata;
	if (pData == NULL)
	{
		return  NPERR_INVALID_INSTANCE_ERROR;
	}

	if (pData->nType == itControl)
	{
		CControlSiteInstance *pSite = pData->pControlSite;
		if (pSite == NULL)
		{
			return NPERR_GENERIC_ERROR;
		}

		HWND hwndParent = (HWND) window->window;
		if (hwndParent)
		{
			RECT rcPos;
			GetClientRect(hwndParent, &rcPos);

			if (pSite->GetParentWindow() == NULL)
			{
				pSite->Attach(hwndParent, rcPos, NULL);
			}
			else
			{
				pSite->SetPosition(rcPos);
			}
		}
}

	return NPERR_NO_ERROR;
}


// NPP_NewStream
//
//	Notifies the plugin of a new data stream.
//  The data type of the stream (a MIME name) is provided.
//  The stream object indicates whether it is seekable.
//  The plugin specifies how it wants to handle the stream.
//
//  In this case, I set the streamtype to be NPAsFile.  This tells the Navigator
//  that the plugin doesn't handle streaming and can only deal with the object as
//  a complete disk file.  It will still call the write functions but it will also
//  pass the filename of the cached file in a later NPE_StreamAsFile call when it
//  is done transfering the file.
//
//  If a plugin handles the data in a streaming manner, it should set streamtype to
//  NPNormal  (e.g. *streamtype = NPNormal)...the NPE_StreamAsFile function will
//  never be called in this case
//
NPError NP_LOADDS
NPP_NewStream(NPP instance,
              NPMIMEType type,
              NPStream *stream, 
              NPBool seekable,
              uint16 *stype)
{
	NG_TRACE_METHOD(NPP_NewStream);

	if(!instance)
	{
		return NPERR_INVALID_INSTANCE_ERROR;
	}

	// save the plugin instance object in the stream instance
	stream->pdata = instance->pdata;
	*stype = NP_ASFILE;
	return NPERR_NO_ERROR;
}


// NPP_StreamAsFile
//
//	The stream is done transferring and here is a pointer to the file in the cache
//	This function is only called if the streamtype was set to NPAsFile.
//
void NP_LOADDS
NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
{
	NG_TRACE_METHOD(NPP_StreamAsFile);

	if(fname == NULL || fname[0] == NULL)
	{
		return;
	}
}


//
//		These next 2 functions are really only directly relevant 
//		in a plug-in which handles the data in a streaming manner.  
//		For a NPAsFile stream, they are still called but can safely 
//		be ignored.
//
//		In a streaming plugin, all data handling would take place here...
//
////\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//.
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\.

int32 STREAMBUFSIZE = 0X0FFFFFFF;   // we are reading from a file in NPAsFile mode
                                    // so we can take any size stream in our write
                                    // call (since we ignore it)
                                

// NPP_WriteReady
//
//	The number of bytes that a plug-in is willing to accept in a subsequent
//	NPO_Write call.
//
int32 NP_LOADDS
NPP_WriteReady(NPP instance, NPStream *stream)
{
	return STREAMBUFSIZE;  
}


// NPP_Write
//
//	Provides len bytes of data.
//
int32 NP_LOADDS
NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
{   
	return len;
}


// NPP_DestroyStream
//
//	Closes a stream object.  
//	reason indicates why the stream was closed.  Possible reasons are
//	that it was complete, because there was some error, or because 
//	the user aborted it.
//
NPError NP_LOADDS
NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
{
	// because I am handling the stream as a file, I don't do anything here...
	// If I was streaming, I would know that I was done and do anything appropriate
	// to the end of the stream...   
	return NPERR_NO_ERROR;
}


// NPP_Print
//
//	Printing the plugin (to be continued...)
//
void NP_LOADDS
NPP_Print(NPP instance, NPPrint* printInfo)
{
	if(printInfo == NULL)   // trap invalid parm
	{
		return;
	}

//    if (instance != NULL) {
//        CPluginWindow* pluginData = (CPluginWindow*) instance->pdata;
//		pluginData->Print(printInfo);
//	}
}

/*******************************************************************************
// NPP_URLNotify:
// Notifies the instance of the completion of a URL request. 
// 
// NPP_URLNotify is called when Netscape completes a NPN_GetURLNotify or
// NPN_PostURLNotify request, to inform the plug-in that the request,
// identified by url, has completed for the reason specified by reason. The most
// common reason code is NPRES_DONE, indicating simply that the request
// completed normally. Other possible reason codes are NPRES_USER_BREAK,
// indicating that the request was halted due to a user action (for example,
// clicking the "Stop" button), and NPRES_NETWORK_ERR, indicating that the
// request could not be completed (for example, because the URL could not be
// found). The complete list of reason codes is found in npapi.h. 
// 
// The parameter notifyData is the same plug-in-private value passed as an
// argument to the corresponding NPN_GetURLNotify or NPN_PostURLNotify
// call, and can be used by your plug-in to uniquely identify the request. 
 ******************************************************************************/

void
NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
{
}

