/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 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/. */

const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;

const MEDIATOR_CONTRACTID =
    "@mozilla.org/appshell/window-mediator;1";
const ASS_CONTRACTID =
    "@mozilla.org/appshell/appShellService;1";
const RDFS_CONTRACTID =
    "@mozilla.org/rdf/rdf-service;1";
const CATMAN_CONTRACTID =
    "@mozilla.org/categorymanager;1";
const PPMM_CONTRACTID =
    "@mozilla.org/parentprocessmessagemanager;1";

const CLINE_SERVICE_CONTRACTID =
    "@mozilla.org/commandlinehandler/general-startup;1?type=chat";
const CLINE_SERVICE_CID =
    Components.ID("{38a95514-1dd2-11b2-97e7-9da958640f2c}");
const STARTUP_CID =
    Components.ID("{ae6ad015-433b-42ab-9afc-1636af5a7fc4}");


Cu.import("chrome://chatzilla/content/lib/js/protocol-handlers.jsm");
/* global ChatZillaProtocols */
/* global IRCProtocolHandlerFactory, IRCSProtocolHandlerFactory */
/* global IRCPROT_HANDLER_CID, IRCSPROT_HANDLER_CID */


function spawnChatZilla(uri, count)
{
    const wm = Cc[MEDIATOR_CONTRACTID].getService(Ci.nsIWindowMediator);
    const ass = Cc[ASS_CONTRACTID].getService(Ci.nsIAppShellService);
    const hiddenWin = ass.hiddenDOMWindow;

    // Ok, not starting currently, so check if we've got existing windows.
    const w = wm.getMostRecentWindow("irc:chatzilla");

    // Claiming that a ChatZilla window is loading.
    if ("ChatZillaStarting" in hiddenWin)
    {
        dump("cz-service: ChatZilla claiming to be starting.\n");
        if (w && ("client" in w) && ("initialized" in w.client) &&
            w.client.initialized)
        {
            dump("cz-service: It lied. It's finished starting.\n");
            // It's actually loaded ok.
            delete hiddenWin.ChatZillaStarting;
        }
    }

    if ("ChatZillaStarting" in hiddenWin)
    {
        count = count || 0;

        if ((new Date() - hiddenWin.ChatZillaStarting) > 10000)
        {
            dump("cz-service: Continuing to be unable to talk to existing window!\n");
        }
        else
        {
            // We have a ChatZilla window, but we're still loading.
            hiddenWin.setTimeout(function wrapper(count) {
                    spawnChatZilla(uri, count + 1);
                }, 250, count);
            return true;
        }
    }

    // We have a window.
    if (w)
    {
        dump("cz-service: Existing, fully loaded window. Using.\n");
        // Window is working and initialized ok. Use it.
        w.focus();
        if (uri)
            w.gotoIRCURL(uri);
        return true;
    }

    dump("cz-service: No windows, starting new one.\n");
    // Ok, no available window, loading or otherwise, so start ChatZilla.
    const args = new Object();
    if (uri)
        args.url = uri;

    hiddenWin.ChatZillaStarting = new Date();
    hiddenWin.openDialog("chrome://chatzilla/content/chatzilla.xul", "_blank",
                 "chrome,menubar,toolbar,status,resizable,dialog=no",
                 args);

    return true;
}


function CommandLineService()
{
}

CommandLineService.prototype =
{
    /* nsISupports */
    QueryInterface(iid)
    {
        if (iid.equals(Ci.nsISupports))
            return this;

        if (Ci.nsICommandLineHandler && iid.equals(Ci.nsICommandLineHandler))
            return this;

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

    /* nsICommandLineHandler */
    handle(cmdLine)
    {
        var uri;
        try
        {
            uri = cmdLine.handleFlagWithParam("chat", false);
        }
        catch (e)
        {
        }

        if (uri || cmdLine.handleFlag("chat", false))
        {
            spawnChatZilla(uri || null)
            cmdLine.preventDefault = true;
        }
    },

    helpInfo: "-chat [<ircurl>]  Start with an IRC chat client.\n",
};


/* factory for command line handler service (CommandLineService) */
const CommandLineFactory =
{
    createInstance(outer, iid)
    {
        if (outer != null)
            throw Cr.NS_ERROR_NO_AGGREGATION;

        return new CommandLineService().QueryInterface(iid);
    },
};


function ProcessHandler()
{
}

ProcessHandler.prototype =
{
    /* nsISupports */
    QueryInterface(iid)
    {
        if (iid.equals(Ci.nsISupports) ||
            iid.equals(Ci.nsIObserver) ||
            iid.equals(Ci.nsIMessageListener))
        {
            return this;
        }

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

    /* nsIObserver */
    observe(subject, topic, data)
    {
        if (topic !== "profile-after-change")
            return;

        const ppmm = Cc[PPMM_CONTRACTID].getService(Ci.nsIMessageBroadcaster);
        ppmm.loadProcessScript("chrome://chatzilla/content/lib/js/chatzilla-protocol-script.js", true);
        ppmm.addMessageListener("ChatZilla:SpawnChatZilla", this);
    },

    /* nsIMessageListener */
    receiveMessage(msg)
    {
        if (msg.name !== "ChatZilla:SpawnChatZilla")
            return;

        spawnChatZilla(msg.data.uri);
    },
};


const StartupFactory =
{
    createInstance(outer, iid)
    {
        if (outer)
            throw Cr.NS_ERROR_NO_AGGREGATION;

        if (!iid.equals(Ci.nsISupports))
            throw Cr.NS_ERROR_NO_INTERFACE;

        // startup:
        return new ProcessHandler();
    },
};


const ChatZillaModule =
{
    registerSelf(compMgr, fileSpec, location, type)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
        const catman = Cc[CATMAN_CONTRACTID].getService(Ci.nsICategoryManager);

        debug("*** Registering -chat handler.\n");
        compMgr.registerFactoryLocation(CLINE_SERVICE_CID,
                                            "ChatZilla CommandLine Service",
                                        CLINE_SERVICE_CONTRACTID,
                                        fileSpec, location, type);
        catman.addCategoryEntry("command-line-argument-handlers",
                                "chatzilla command line handler",
                                CLINE_SERVICE_CONTRACTID, true, true);
        catman.addCategoryEntry("command-line-handler",
                                "m-irc",
                                CLINE_SERVICE_CONTRACTID, true, true);

        debug("*** Registering irc protocol handler.\n");
            ChatZillaProtocols.initObsolete(compMgr, fileSpec, location, type);

        debug("*** Registering done.\n");
    },

    unregisterSelf(compMgr, fileSpec, location)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);

        const catman = Cc[CATMAN_CONTRACTID].getService(Ci.nsICategoryManager);
        catman.deleteCategoryEntry("command-line-argument-handlers",
                                   "chatzilla command line handler", true);
        catman.deleteCategoryEntry("command-line-handler",
                                   "m-irc", true);
    },

    getClassObject(compMgr, cid, iid)
    {
        // Checking if we're disabled in the Chrome Registry.
        var rv;
        try
        {
            const rdfSvc = Cc[RDFS_CONTRACTID].getService(Ci.nsIRDFService);
            const rdfDS = rdfSvc.GetDataSource("rdf:chrome");
            const resSelf = rdfSvc.GetResource("urn:mozilla:package:chatzilla");
            const resDisabled = rdfSvc.GetResource("http://www.mozilla.org/rdf/chrome#disabled");
            rv = rdfDS.GetTarget(resSelf, resDisabled, true);
        }
        catch (e)
        {
        }
        if (rv)
            throw Cr.NS_ERROR_NO_INTERFACE;

        if (cid.equals(CLINE_SERVICE_CID))
            return CommandLineFactory;

        if (cid.equals(IRCPROT_HANDLER_CID))
            return IRCProtocolHandlerFactory;

        if (cid.equals(IRCSPROT_HANDLER_CID))
            return IRCSProtocolHandlerFactory;

        if (cid.equals(STARTUP_CID))
            return StartupFactory;

        if (!iid.equals(Ci.nsIFactory))
            throw Cr.NS_ERROR_NOT_IMPLEMENTED;

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

    canUnload(compMgr)
    {
        return true;
    },
};


/* entrypoint */
function NSGetModule(compMgr, fileSpec)
{
    return ChatZillaModule;
}

function NSGetFactory(cid)
{
    return ChatZillaModule.getClassObject(null, cid, null);
}
