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

#ifndef nsComponentManager_h__
#define nsComponentManager_h__

#include "nsIComponentLoader.h"
#include "nsNativeComponentLoader.h"
#include "nsIComponentManager.h"
#include "nsIFactory.h"
#include "nsIRegistry.h"
#include "nsIInterfaceRequestor.h"
#include "nsHashtable.h"
#include "prtime.h"
#include "prmon.h"
#include "nsCOMPtr.h"
#include "nsWeakReference.h"
#include "nsXPIDLString.h"
#include "nsIFile.h"

class nsFactoryEntry;
class nsDll;
class nsIServiceManager;

// Registry Factory creation function defined in nsRegistry.cpp
// We hook into this function locally to create and register the registry
// Since noone outside xpcom needs to know about this and nsRegistry.cpp
// does not have a local include file, we are putting this definition
// here rather than in nsIRegistry.h
extern "C" NS_EXPORT nsresult NS_RegistryGetFactory(nsIFactory** aFactory);

extern const char XPCOM_LIB_PREFIX[];

////////////////////////////////////////////////////////////////////////////////

class nsComponentManagerImpl
    : public nsIComponentManager, public nsSupportsWeakReference,
      public nsIInterfaceRequestor {
public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSICOMPONENTMANAGER
    NS_DECL_NSIINTERFACEREQUESTOR

    // nsComponentManagerImpl methods:
    nsComponentManagerImpl();
    virtual ~nsComponentManagerImpl();

    static nsComponentManagerImpl* gComponentManager;
    nsresult Init(void);
    nsresult PlatformPrePopulateRegistry();
    nsresult Shutdown(void);

    friend class nsFactoryEntry;
protected:
    nsresult RegistryNameForLib(const char *aLibName, char **aRegistryName);
    nsresult RegisterComponentCommon(const nsCID &aClass,
                                     const char *aClassName,
                                     const char *aProgID,
                                     const char *aRegistryName,
                                     PRBool aReplace, PRBool aPersist,
                                     const char *aType);
    nsresult AddComponentToRegistry(const nsCID &aCID, const char *aClassName,
                                    const char *aProgID,
                                    const char *aRegistryName,
                                    const char *aType);
    nsresult GetLoaderForType(const char *aType, 
                              nsIComponentLoader **aLoader);

    nsresult LoadFactory(nsFactoryEntry *aEntry, nsIFactory **aFactory);
    nsFactoryEntry *GetFactoryEntry(const nsCID &aClass, PRBool checkRegistry);

    nsresult SyncComponentsInDir(PRInt32 when, nsIFile *dirSpec);
    nsresult SelfRegisterDll(nsDll *dll);
    nsresult SelfUnregisterDll(nsDll *dll);
    nsresult HashProgID(const char *aprogID, const nsCID &aClass);
    nsresult UnloadLibraries(nsIServiceManager *servmgr, PRInt32 when);

    // The following functions are the only ones that operate on the persistent
    // registry
    nsresult PlatformInit(void);
    nsresult PlatformVersionCheck(nsRegistryKey *aXPCOMRootKey);
    nsresult PlatformRegister(const char *cidString, const char *className, const char *progID, nsDll *dll);
    nsresult PlatformUnregister(const char *cidString, const char *aLibrary);
    nsresult PlatformFind(const nsCID &aCID, nsFactoryEntry* *result);
    nsresult PlatformProgIDToCLSID(const char *aProgID, nsCID *aClass);
    nsresult PlatformCLSIDToProgID(const nsCID *aClass, char* *aClassName, char* *aProgID);

protected:
    nsObjectHashtable*  mFactories;
    nsObjectHashtable*  mProgIDs;
    nsSupportsHashtable*  mLoaders;
    PRMonitor*          mMon;
    nsIRegistry*        mRegistry;
    nsRegistryKey       mXPCOMKey;
    nsRegistryKey       mClassesKey;
    nsRegistryKey       mCLSIDKey;
    PRBool              mPrePopulationDone;
    nsRegistryKey       mLoadersKey;
    nsNativeComponentLoader *mNativeComponentLoader;
    nsCOMPtr<nsIFile>   mComponentsDir;
    PRInt32             mComponentsOffset;

    // Shutdown
    #define NS_SHUTDOWN_NEVERHAPPENED 0
    #define NS_SHUTDOWN_INPROGRESS 1
    #define NS_SHUTDOWN_COMPLETE 2
    PRUint32 mShuttingDown;
};

#define NS_MAX_FILENAME_LEN	1024

#define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24)

#ifdef XP_UNIX
/* The default registry on the unix system is $HOME/.mozilla/registry per
 * vr_findGlobalRegName(). vr_findRegFile() will create the registry file
 * if it doesn't exist. But it wont create directories.
 *
 * Hence we need to create the directory if it doesn't exist already.
 *
 * Why create it here as opposed to the app ?
 * ------------------------------------------
 * The app cannot create the directory in main() as most of the registry
 * and initialization happens due to use of static variables.
 * And we dont want to be dependent on the order in which
 * these static stuff happen.
 *
 * Permission for the $HOME/.mozilla will be Read,Write,Execute
 * for user only. Nothing to group and others.
 */
#define NS_MOZILLA_DIR_NAME		".mozilla"
#define NS_MOZILLA_DIR_PERMISSION	00700
#endif /* XP_UNIX */

#ifdef XP_BEOS
#define NS_MOZILLA_DIR_NAME		"mozilla"
#define NS_MOZILLA_DIR_PERMISSION	00700
#endif /* XP_BEOS */

/**
 * When using the registry we put a version number in it.
 * If the version number that is in the registry doesn't match
 * the following, we ignore the registry. This lets news versions
 * of the software deal with old formats of registry and not
 *
 * alpha0.20 : First time we did versioning
 * alpha0.30 : Changing autoreg to begin registration from ./components on unix
 * alpha0.40 : repository -> component manager
 * alpha0.50 : using nsIRegistry
 * alpha0.60 : xpcom 2.0 landing
 * alpha0.70 : using nsIFileSpec. PRTime -> PRUint32
 * alpha0.90 : using nsIComponentLoader, abs:/rel:/lib:, shaver-cleanup
 * alpha0.92 : restructured registry keys
 */
#define NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING "alpha0.92"

////////////////////////////////////////////////////////////////////////////////
/**
 * Class: nsFactoryEntry()
 *
 * There are two types of FactoryEntries.
 *
 * 1. {CID, dll} mapping.
 *		Factory is a consequence of the dll. These can be either session
 *		specific or persistent based on whether we write this
 *		to the registry or not.
 *
 * 2. {CID, factory} mapping
 *		These are strictly session specific and in memory only.
 */

class nsFactoryEntry {
public:
    nsFactoryEntry(const nsCID &aClass, const char *location,
                   const char *aType, nsIComponentLoader *aLoader);
    nsFactoryEntry(const nsCID &aClass, nsIFactory *aFactory);
    ~nsFactoryEntry();

    nsresult GetFactory(nsIFactory **aFactory, 
                        nsComponentManagerImpl * mgr) {
        if (factory) {
            *aFactory = factory.get();
            NS_ADDREF(*aFactory);
            return NS_OK;
        }

        nsresult rv;
        if (!loader.get()) {
            rv = mgr->GetLoaderForType(type, getter_AddRefs(loader));
            if(NS_FAILED(rv))
                return rv;
        }

        rv = loader->GetFactory(cid, location, type, aFactory);
        if (NS_SUCCEEDED(rv))
            factory = do_QueryInterface(*aFactory);
        return rv;
    }

    nsCID cid;
    nsXPIDLCString location;
    nsCOMPtr<nsIFactory> factory;
    nsXPIDLCString type;
    nsCOMPtr<nsIComponentLoader> loader;
};

////////////////////////////////////////////////////////////////////////////////

#endif // nsComponentManager_h__
