/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 JIM.
 *
 * The Initial Developer of the Original Code is
 * Pawel Chmielowski <prefiks@o2.pl>
 *
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Pawel Chmielowski <prefiks@o2.pl>
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * JabberService
 *
 *
 */
function JabberService()
{
    var profileDirectory = Components.classes["@mozilla.org/file/directory_service;1"].
        getService(Components.interfaces.nsIProperties).
            get("ProfD", Components.interfaces.nsIFile).path;
    var file;

    this.base = Observable;
    this.base();

    if (!(this.settingsDirectory = new File(profileDirectory, "jim")).exists)
        this.settingsDirectory.createDirectory(0700);
    this.settingsDirectory = this.settingsDirectory.path;

    file = new File(this.settingsDirectory, "config.rdf");
    this.configRDF = new FileRDF(file, JIM_NAMESPACE_URI+"rdf/config#", "jim");
    this.configRes = this.configRDF.getResource("rdf:jim-config");

    if ("@DEBUG@") {
        file = new File(this.settingsDirectory, "localrdf.rdf");
        this.localRDF = new FileRDF(file, JIM_NAMESPACE_URI+"rdf/base#", "jim");
    } else
        this.localRDF = new MemoryRDF(JIM_NAMESPACE_URI+"rdf/base#");

    this.socket = new Socket(this);
    this.core = new CoreService(this);
    this.msgId = 1;
    this.connected = false;
    this.appInfo = {
        name: "JIM",
        version: "pre0.1(20030508)",
        os: window.navigator.userAgent
    }

    this.socket.addObserver(this);
    this.socket.setOutputCharset("UTF-8");
    this.currNode = document.implementation.createDocument(null, null, null);
    this.xmlDOMBuilder = new XMLDOMBuilder(this.currNode);
}

function ModuleLoader()
{
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    this.incpath = "chrome://jim/content/scripts";
    this.loadedscripts = {};
    this.loader = new (new Components.Constructor("@mozilla.org/moz/jssubscript-loader;1",
        "mozIJSSubScriptLoader"));
}

ModuleLoader.prototype =
{
    load: function(script)
    {
        var i, paths;

        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

        if (script in this.loadedscripts)
            return Components.results.NS_OK;

        paths = this.incpath.split("@");

        for (i = 0; i < paths.length; i++) {
            try {
                this.loadedscripts[script] = 1;
                this.loader.loadSubScript(paths[i]+"/"+script);
                return Components.results.NS_OK;
            } catch (ex) {
                alert("[Exception:"+ (ex.message ? (ex.message +" on "+ex.fileName+":"+ex.lineNumber) : ex) + "]");
                delete this.loadedscripts[script];
            }
        }
        return Components.results.NS_ERROR_INVALID_ARG;
    },

    addIncPath: function(path)
    {
        this.incpath += "@"+path;
    }
}

var ML = new ModuleLoader();

ML.load("utils/observable.js");
ML.load("net/socket.js");
ML.load("xml/xmldombuilder.js");
ML.load("services/core.js");
ML.load("rdf/filerdf.js");
ML.load("rdf/memoryrdf.js");

JabberService.prototype =
{
    __proto__: Observable.prototype,
    pingTimeout: 30*1000,
    connectionTimeout: 30*1000,
    currNode: null,

    connect: function(host, port, useSSL, proxyHost, proxyPort)
    {
        var socketTypes = new Array();

        if (this.connected)
            this.disconnect();

        while (DB.doc.hasChildNodes())
            DB.doc.removeChild(DB.doc.firstChild);

        this.currNode = DB.doc;

        this.host = host;
        this.port = port;
        this.useSSL = useSSL;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;

        if (useSSL) {
            socketTypes.push("ssl")
        }

        if (this.connected)
            this.disconnect();
        this.socket.open(host, port, socketTypes);
        this.__connTimeout = setTimeout(this.__onConnTimeout, this.connectionTimeout, this);
        this.__pingTimeout = setInterval(this.__ping, this.pingTimeout, this);
    },

    reconnect: function()
    {
        if ("host" in this && "port" in this)
            this.connect(this.host, this.port, this.useSSL, this.proxyHost, this.proxyPort)
        else
            throw "JabberService:Connection parameter not specified.";
    },

    disconnect: function(show)
    {
        if (this.connected) {
            clearTimeout(this.__connTimeout);
            clearTimeout(this.__pingTimeout);
            this.core.presence(null, null, "unavailable", show);
            this.send(DB.createElement(2, "http://etherx.jabber.org/streams", "stream:stream"));
            this.socket.close();
        }
    },

    clean: function()
    {
        this.fireEvent("onClean");
        this.socket.removeObserver(this);
        this.disconnect();
    },

    send: function(tag) {
        if (this.currNode.nodeType == this.currNode.ELEMENT_NODE)
            this.currNode.addDOM(tag);
        else
            this.currNode.appendChild(tag);

        if (!tag.hasAttribute("id"))
            tag.setAttribute("id", "m"+(this.msgId++));
        this.fireEvent("onSend", tag);
        this.sendData(domToString(tag));

        if (tag.type == 1) {
            this.currNode = tag;
        } else {
            this.currNode.removeChild(tag);
            if (tag.type == 2)
                this.currNode = this.currNode.parentNode;
        }

        return tag.getAttribute("id");
    },

    setErrorHandler: function(handler)
    {
        this.handler = handler;
    },

    handleError: function()
    {
        if (this.handler && this.handler.handleError)
            this.handler.handleError.apply(this.handler, arguments);
    },

    sendData: function(data)
    {
        this.fireEvent("onSendData", data);
        this.socket.write(data);
    },

    __ping: function(_this)
    {
        _this.sendData(" ");
    },

    __onConnTimeout: function(_this)
    {
        _this.disconnect();
    },

    __gotResponse: function()
    {
        clearTimeout(this.__connTimeout);
        this.fireEvent("onFirstResponse");
    },

    onConnectionClose: function(statusCode) {
        this.connected = false;
        this.fireEvent("onDisconnect", statusCode);
    },

    onConnectionOpen: function() {
        this.connectionTime = new Date();
        this.connected = true;
        this.fireEvent("onConnectFirst");
        this.send(DB.createElement(1, "http://etherx.jabber.org/streams", "stream:stream",
            "to", this.host,
            "xmlns", "jabber:client")
        );
        this.fireEvent("onConnect");
    }
}

const JIM_NAMESPACE_URI = "http://jim.mozdev.org/"

if (!("jabberService" in window))
    if ("opener" in window && "jabberService" in window.opener)
        window.jabberService = window.opener.jabberService;
    else
        window.jabberService = new JabberService();

var DB = window.jabberService.xmlDOMBuilder;