# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# NOTE: Although this generates headers and code for C++, using Services.h
# is deprecated in favour of Components.h.


services = []


def service(name, iface, contractid):
    """Define a convenient service getter"""
    services.append((name, iface, contractid))


# The `name` parameter is derived from the `iface` by removing the `nsI`
# prefix. (This often matches the `contractid`, but not always.)
service("ChromeRegistry", "nsIChromeRegistry", "@mozilla.org/chrome/chrome-registry;1")
service("IOService", "nsIIOService", "@mozilla.org/network/io-service;1")
service("ObserverService", "nsIObserverService", "@mozilla.org/observer-service;1")
service("PermissionManager", "nsIPermissionManager", "@mozilla.org/permissionmanager;1")
service(
    "AsyncShutdownService",
    "nsIAsyncShutdownService",
    "@mozilla.org/async-shutdown-service;1",
)

# The definition file needs access to the definitions of the particular
# interfaces. If you add a new interface here, make sure the necessary includes
# are also listed in the following code snippet.
CPP_INCLUDES = """
#include "mozilla/Likely.h"
#include "mozilla/Services.h"
#include "mozIThirdPartyUtil.h"
#include "nsComponentManager.h"
#include "nsIObserverService.h"
#include "nsNetCID.h"
#include "nsObserverService.h"
#include "nsXPCOMPrivate.h"
#include "nsIIOService.h"
#include "nsIDirectoryService.h"
#include "nsIChromeRegistry.h"
#include "nsIStringBundle.h"
#include "nsIToolkitChromeRegistry.h"
#include "IHistory.h"
#include "nsIXPConnect.h"
#include "nsIPermissionManager.h"
#include "nsIPrefService.h"
#include "nsIServiceWorkerManager.h"
#include "nsICacheStorageService.h"
#include "nsIStreamTransportService.h"
#include "nsISocketTransportService.h"
#include "nsIURIClassifier.h"
#include "nsIHttpActivityObserver.h"
#include "nsIAsyncShutdown.h"
#include "nsIUUIDGenerator.h"
#include "nsIGfxInfo.h"
#include "nsIURIFixup.h"
#include "nsIBits.h"
#include "nsIXULRuntime.h"
"""


#####
# Codegen Logic
#
# The following code consumes the data listed above to generate the files
# Services.h, and Services.cpp which provide access to these service getters in
# C++ code.


def services_h(output):
    output.write(
        """\
/* THIS FILE IS GENERATED BY Services.py - DO NOT EDIT */

#ifndef mozilla_Services_h
#define mozilla_Services_h

#include "nscore.h"
#include "nsCOMPtr.h"
"""
    )

    for (name, iface, contractid) in services:
        # Write out a forward declaration for the type in question
        segs = iface.split("::")
        for namespace in segs[:-1]:
            output.write("namespace %s {\n" % namespace)
        output.write("class %s;\n" % segs[-1])
        for namespace in reversed(segs[:-1]):
            output.write("} // namespace %s\n" % namespace)

        # Write out the C-style function signature, and the C++ wrapper
        output.write(
            """
#ifdef MOZILLA_INTERNAL_API
namespace mozilla {
namespace services {
/**
 * Fetch a cached instance of the %(name)s.
 * This function will return nullptr during XPCOM shutdown.
 * Prefer using static components to this method.
 * WARNING: This method is _not_ threadsafe!
 */
already_AddRefed<%(type)s> Get%(name)s();
} // namespace services
} // namespace mozilla
#endif // defined(MOZILLA_INTERNAL_API)
"""
            % {
                "name": name,
                "type": iface,
            }
        )

    output.write("#endif // !defined(mozilla_Services_h)\n")


def services_cpp(output):
    output.write(
        """\
/* THIS FILE IS GENERATED BY Services.py - DO NOT EDIT */
"""
    )
    output.write(CPP_INCLUDES)

    for (name, iface, contractid) in services:
        output.write(
            """
static %(type)s* g%(name)s = nullptr;

namespace mozilla {
namespace services {
already_AddRefed<%(type)s> Get%(name)s()
{
  if (MOZ_UNLIKELY(gXPCOMShuttingDown)) {
    return nullptr;
  }
  if (!g%(name)s) {
    nsCOMPtr<%(type)s> os = do_GetService("%(contractid)s");
    os.swap(g%(name)s);
  }
  return do_AddRef(g%(name)s);
}
}
}
"""
            % {
                "name": name,
                "type": iface,
                "contractid": contractid,
            }
        )

    output.write(
        """
/**
 * Clears service cache, sets gXPCOMShuttingDown
 */
void
mozilla::services::Shutdown()
{
  gXPCOMShuttingDown = true;
"""
    )
    for (name, iface, contractid) in services:
        output.write("  NS_IF_RELEASE(g%s);\n" % name)
    output.write("}\n")
