#include "chxmimemanager.h"
#include "ihxmimeassocmanager.h"
#include "ihxmimeinfo.h"
#include "ihxappinfo.h"

/* Plugin handler */
#include "hxplugn.h"
#include "hxphand.h"

/* File format info */
#include "hxformt.h"

#include "hxassert.h"


CHXMimeManager::CHXMimeManager()
{
}


CHXMimeManager::~CHXMimeManager()
{
    Reset();
}


void
CHXMimeManager::Reset()
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	IHXMimeAssocManager* pMimeMgr = (IHXMimeAssocManager*)*i;
	HX_ASSERT(pMimeMgr != NULL);
	pMimeMgr->Reload();
	HX_RELEASE(pMimeMgr);
    }

    for (i = m_mapMimeTypes.Begin(); i != m_mapMimeTypes.End(); ++i)
    {
	IHXFileFormatObject* pFileFormat = (IHXFileFormatObject*)*i;
	HX_ASSERT(pFileFormat != NULL);
	HX_RELEASE(pFileFormat);
    }
}


void
CHXMimeManager::Commit()
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	IHXMimeAssocManager* pMimeMgr = (IHXMimeAssocManager*)*i;
	HX_ASSERT(pMimeMgr != NULL);
	pMimeMgr->Commit();
    }
}


void
CHXMimeManager::Reload()
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	IHXMimeAssocManager* pMimeMgr = (IHXMimeAssocManager*)*i;
	HX_ASSERT(pMimeMgr != NULL);
	pMimeMgr->Reload();
    }
}


HX_RESULT
CHXMimeManager::Init(IUnknown* pContext)
{
    HX_RESULT res = HXR_FAIL;

    IHXPlugin2Handler* pPlugin2Handler = NULL;
    if (pContext)
    {
	res = pContext->QueryInterface(IID_IHXPlugin2Handler,
				       (void**)&pPlugin2Handler);
	if (SUCCEEDED(res))
	{
	    IHXPluginEnumerator* pPluginEnumerator = NULL;
	    res = pPlugin2Handler->QueryInterface(IID_IHXPluginEnumerator,
						  (void**)&pPluginEnumerator);
	    if (SUCCEEDED(res))
	    {
		UINT32 uCount = pPluginEnumerator->GetNumOfPlugins();
		for (UINT32 i = 0; i != uCount; ++i)
		{
		    IUnknown* pUnk = NULL;
		    if (SUCCEEDED(pPluginEnumerator->GetPlugin(i, pUnk)))
		    {
			// is this one of our mime assoc manager plugins?
			IHXMimeAssocManager* pMimeMgr = NULL;
			if (SUCCEEDED(pUnk->QueryInterface(
					  IID_IHXMimeAssocManager,
					  (void**)&pMimeMgr)))
			{
			    // what's this mime manager's name?
			    const char* pName = NULL;
			    const char* pDesc = NULL;
			    UINT32 uVer = 0;
			    HX_VERIFY(SUCCEEDED(pMimeMgr->GetMimeSystemInfo(
						    pName, pDesc, uVer)));
			    HX_ASSERT(pName);

			    // check for dupes, avoid memory leaks
			    void* pIgnore;
			    if (m_mapMimeMgrs.Lookup(pName, pIgnore))
			    {
				HX_ASSERT(FALSE);
				HX_RELEASE(pMimeMgr);
			    }
			    else
			    {
				// store it in our map
				pMimeMgr->Init();
				m_mapMimeMgrs.SetAt(pName, pMimeMgr);
			    }
			}

			// is this a file format?
			IHXFileFormatObject* pFileFormat = NULL;
			if (SUCCEEDED(pUnk->QueryInterface(
					  IID_IHXFileFormatObject,
					  (void**)&pFileFormat)))
			{
			    const char** ppMimeTypes = NULL;
			    const char** ppExtensions = NULL;
			    const char** ppOpenNames = NULL;
			    HX_VERIFY(SUCCEEDED(pFileFormat->GetFileFormatInfo(
						    ppMimeTypes,
						    ppExtensions,
						    ppOpenNames)));
			    while (*ppMimeTypes)
			    {
				void* pIgnore = NULL;
				if (!m_mapMimeTypes.Lookup(*ppMimeTypes,
							  pIgnore))
				{
				    // XXXNH: uncomment this if you want to
				    // dump all the mime types we can handle
				    // to stdout
				    //fprintf(stdout, "%s\n", *ppMimeTypes);
				    pFileFormat->AddRef();
				    m_mapMimeTypes.SetAt(*ppMimeTypes,
							 pFileFormat);
				}
				    ++ppMimeTypes;
			    }
			    HX_RELEASE(pFileFormat);
			}
			HX_RELEASE(pUnk);
		    } // for i...
		}
		HX_RELEASE(pPluginEnumerator);
	    }
	    HX_RELEASE(pPlugin2Handler);
	}
    }
    return res;
}


IHXMimeAssocManager*
CHXMimeManager::GetMimeAssocManager(const char* pName)
{
    IHXMimeAssocManager* pMimeMgr = NULL;
    if (m_mapMimeMgrs.Lookup(pName, (void*&)pMimeMgr))
    {
	HX_ASSERT(pMimeMgr);
	pMimeMgr->AddRef();
    }
    return pMimeMgr;
}


IHXFileFormatObject*
CHXMimeManager::GetFileFormat(const char* pMimeType)
{
    IHXFileFormatObject* pFileFormat = NULL;
    if (m_mapMimeTypes.Lookup(pMimeType, (void*&)pFileFormat))
    {
	HX_ASSERT(pFileFormat);
	pFileFormat->AddRef();
    }
    return pFileFormat;
}


void
CHXMimeManager::SetupMimeType(const char*  pMimeType,
			      const char** ppExtensions,
			      const char** ppOpenNames,
			      const char*  pIconPath,
			      BOOL bOverride)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	SetupMimeType((IHXMimeAssocManager*)*i, pMimeType, ppExtensions, 
		      ppOpenNames, pIconPath, bOverride);
    }
}


void
CHXMimeManager::SetupApp(const char* pAppId,
			 const char* pName,
			 const char* pCommand,
			 BOOL bCanOpenMultiple,
			 BOOL bRequiresTerminal)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	SetupApp((IHXMimeAssocManager*)*i, pAppId, pName, pCommand,
		 bCanOpenMultiple, bRequiresTerminal);
    }
}


void
CHXMimeManager::SetAssoc(const char* pMime, const char* pApp, BOOL bOverride)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	SetAssoc((IHXMimeAssocManager*)*i, pMime, pApp, bOverride);
    }
}


void
CHXMimeManager::SetupMimeType(IHXMimeAssocManager* pMimeMgr,
			      const char*  pMimeType,
			      const char** ppExtensions,
			      const char** ppOpenNames,
			      const char*  pIconPath,
			      BOOL bOverride)
{
    HX_ASSERT(pMimeMgr);

    IHXMimeInfo* pMimeInfo = NULL;
    if (SUCCEEDED(pMimeMgr->GetMime(pMimeType, pMimeInfo)))
    {
	if (bOverride || !pMimeInfo->GetIcon())
	{
	    pMimeInfo->SetIcon(pIconPath);
	}
/* XXXNH: where do we get the name/desc from? */
	if (bOverride || !pMimeInfo->GetName())
	{
	    pMimeInfo->SetName(pMimeType); //pDescription);
	}

	// make sure we don't add any duplicate extensions
	UINT32 uCount = 0;
	const char** ppExistingExts = NULL;
	pMimeInfo->GetExts(ppExistingExts, uCount);

	if (bOverride)
	{
	    // clear existing extensions
	    for (const char** pCursor = ppExistingExts; 
		 pCursor != ppExistingExts + uCount; ++pCursor)
	    {
		pMimeInfo->RemoveExt(*pCursor);
	    }
	    uCount = 0;
	}

	// add our extensions
	while (*ppExtensions)
	{
	    BOOL bFound = FALSE;
/*	    if (uCount)
	    {
		for (const char** pCursor = ppExistingExts; 
		     !bFound && pCursor != ppExistingExts + uCount; ++pCursor)
		{
		    if (strcasecmp(*pCursor, *ppExtensions) == 0)
		    {
			bFound = TRUE;
		    }
		}
		}*/

	    if (!bFound)
	    {
		pMimeInfo->AddExt(*ppExtensions);
	    }
	    ++ppExtensions;
	}
	HX_RELEASE(pMimeInfo);
    }
}


void
CHXMimeManager::SetupApp(IHXMimeAssocManager* pMimeMgr,
			 const char* pAppId,
			 const char* pName,
			 const char* pCommand,
			 BOOL bCanOpenMultiple,
			 BOOL bRequiresTerminal)
{
    HX_ASSERT(pMimeMgr);

    IHXAppInfo* pAppInfo = NULL;
    if (SUCCEEDED(pMimeMgr->GetApp(pAppId, pAppInfo)))
    {
	pAppInfo->SetName(pName);
	pAppInfo->SetCommand(pCommand);
	pAppInfo->SetCanOpenMultiple(bCanOpenMultiple);
	pAppInfo->SetRequiresTerminal(bRequiresTerminal);
	HX_RELEASE(pAppInfo);

	HX_ASSERT(pMimeMgr->AppExists(pAppId));
    }
}


void
CHXMimeManager::SetAssoc(IHXMimeAssocManager* pMimeMgr,
			 const char* pMime,
			 const char* pApp,
			 BOOL bOverride)
{
    HX_ASSERT(pMimeMgr);

    HX_ASSERT(pMimeMgr->AppExists(pApp));
    HX_ASSERT(pMimeMgr->MimeExists(pMime));
    const char* pOldApp = NULL;
    if (bOverride || FAILED(pMimeMgr->GetAssoc(pMime, pOldApp)))
    {
	pMimeMgr->SetAssoc(pMime, pApp);
    }
}


POSITION
CHXMimeManager::GetFirstMimeAssocManager(REF(const char*) pName, 
					 REF(IHXMimeAssocManager*) pMimeMgr)
{
    POSITION p = m_mapMimeMgrs.GetStartPosition();
    if (!p)
    {
	pName = NULL;
	pMimeMgr = NULL;
	return NULL;
    }
    return GetNextMimeAssocManager(p, pName, pMimeMgr);
}


POSITION
CHXMimeManager::GetNextMimeAssocManager(POSITION p,
					REF(const char*) pName, 
					REF(IHXMimeAssocManager*) pMimeMgr)
{
    HX_ASSERT(p);
    if (p)
    {
	m_mapMimeMgrs.GetNextAssoc(p, pName, (void*&)pMimeMgr);
	HX_ASSERT(pMimeMgr);
	pMimeMgr->AddRef();
    }
    return p;
}


POSITION
CHXMimeManager::GetFirstFileFormat(REF(const char*) pMimeType, 
				   REF(IHXFileFormatObject*) pFileFormat)
{
    POSITION p = m_mapMimeTypes.GetStartPosition();
    return GetNextFileFormat(p, pMimeType, pFileFormat);
}


POSITION
CHXMimeManager::GetNextFileFormat(POSITION p,
				  REF(const char*) pMimeType, 
				  REF(IHXFileFormatObject*) pFileFormat)
{
    HX_ASSERT(p);
    if (p)
    {
	m_mapMimeTypes.GetNextAssoc(p, pMimeType, (void*&)pFileFormat);
	HX_ASSERT(pFileFormat);
	pFileFormat->AddRef();
    }
    return p;
}



void
CHXMimeManager::SetupAllFileFormats(const char* pIconPath, BOOL bOverride)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeTypes.Begin(); i != m_mapMimeTypes.End(); ++i)
    {
	SetupFileFormat((IHXFileFormatObject*)*i, pIconPath, bOverride);
    }
}


void
CHXMimeManager::SetupFileFormat(IHXFileFormatObject* pFileFormat,
				const char* pIconPath,
				BOOL bOverride)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	SetupFileFormat((IHXMimeAssocManager*)*i, pFileFormat, pIconPath, 
			bOverride);
    }
}


void
CHXMimeManager::SetupFileFormat(IHXMimeAssocManager* pMimeMgr,
				IHXFileFormatObject* pFileFormat,
				const char* pIconPath,
				BOOL bOverride)
{
    const char** ppMimeTypes = NULL;
    const char** ppExtensions = NULL;
    const char** ppOpenNames = NULL;
    if (SUCCEEDED(pFileFormat->GetFileFormatInfo(ppMimeTypes,
						 ppExtensions,
						 ppOpenNames)))
    {
	while (*ppMimeTypes)
	{
	    SetupMimeType(pMimeMgr, *ppMimeTypes, ppExtensions, ppOpenNames,
			  pIconPath, bOverride);
	    ++ppMimeTypes;
	}
    }
}



void
CHXMimeManager::SetMatchingAssocs(const char* pMatch,
				  const char* pApp,
				  BOOL bOverride)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeTypes.Begin(); i != m_mapMimeTypes.End(); ++i)
    {
	const char* pMimeType = (const char*)i.get_key();
	if (strstr(pMimeType, pMatch))
	{
	    SetAssoc(pMimeType, pApp, bOverride);
	}
    }
}



void
CHXMimeManager::SetupProtocol(const char* pMimeType,
			      const char* pProtocol,
			      const char* pDescription,
			      const char* pIconPath)
{
    CHXMapStringToOb::Iterator i;
    for (i = m_mapMimeMgrs.Begin(); i != m_mapMimeMgrs.End(); ++i)
    {
	SetupProtocol((IHXMimeAssocManager*)*i, pMimeType, pProtocol,
		      pDescription, pIconPath);
    }
}



void
CHXMimeManager::SetupProtocol(IHXMimeAssocManager* pMimeMgr,
			      const char* pMimeType,
			      const char* pProtocol,
			      const char* pDescription,
			      const char* pIconPath)
{
    HX_ASSERT(pMimeMgr);

    IHXMimeInfo* pMimeInfo = NULL;
    if (SUCCEEDED(pMimeMgr->GetMime(pMimeType, pMimeInfo)))
    {
	pMimeInfo->SetName(pDescription);
	pMimeInfo->SetProtocol(pProtocol);
	pMimeInfo->SetIcon(pIconPath);
    }
}

