/* ***** 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 ***** */

/**
 * services.highlevel.HLAvatars
 *
 *
 */
ML.load("utils/observable.js");
ML.load("io/file.js");
ML.load("rdf/filerdf.js");
ML.load("utils/base64.js");
ML.load("utils/misc.js");

/**
 * HLAvatars
 *   Service which controls information about avatars
 *
 * Constructors:
 *   HLAvatars(jabberService) - constructor
 *
 * Events:
 *   onAvatarChanged(users, userid, resourceid)
 *   onAvatarThisChanged(thisUser)
 *
 */

function HLAvatars(jabberService)
{
    this.base = Observable;
    this.base();
    var nodes, node;
    var locNodes, locNode;
    var avatarsDir, file;
    var arr, i, rdf;
    var hash, url;

    avatarsDir = new File(jabberService.settingsDirectory, "avatars");
    if (!avatarsDir.exists)
        avatarsDir.createDirectory(0700);

    this.jabberService = jabberService;
    this.avatarsHash = { };
    this.avatarsUriHash = { };
    this.avatarsDir = avatarsDir.path;

    locNodes = jabberService.localRDF.getContainer("rdf:jim-avatars", "seq");
    locNodes.removeElements(true);

    file = new File(jabberService.settingsDirectory, "avatars.rdf");
    this.rdf = new FileRDF(file, JIM_NAMESPACE_URI+"rdf/avatars#", "av");

    nodes = this.rdf.getContainer("rdf:jim-avatars", "seq").
        getElements();

    jabberService.localRDF.ns = JIM_NAMESPACE_URI+"rdf/avatars#";

    while (nodes.hasNext()) {
        node = nodes.getNext();

        url = node.getTarget("url").value;
        hash = node.getTarget("hash").value;

        this.avatarsHash[hash] = url;
        this.avatarsUriHash[url] = hash;

        locNode = jabberService.localRDF.getResource();
        locNode.setAssertion("url", url);
        locNodes.appendElement(locNode);
    }

    rdf = new FileRDF("chrome://jim/content/data/avatars/avatars.rdf",
        JIM_NAMESPACE_URI+"rdf/avatars#");

    nodes = rdf.getContainer("rdf:jim-avatars").getElements();
    while (nodes.hasNext()) {
        node = nodes.getNext();

        url = node.getTarget("url").value;
        hash = node.getTarget("hash").value;

        this.avatarsHash[hash] = url;
        this.avatarsUriHash[url] = hash;

        locNode = jabberService.localRDF.getResource();
        locNode.setAssertion("url", url);
        locNodes.appendElement(locNode);
    }

    jabberService.core.iq.avatar.addObserver(this);
    jabberService.core.iq.privat.addObserver(this);
    jabberService.core.addObserver(this);
    jabberService.addObserver(this);
}

HLAvatars.prototype =
{
    __proto__: Observable.prototype,

    getAvatarByLongJID: function(longJID)
    {
        var resource = getResourceIDByLongJID(longJID);

        if (!resource)
            return null;

        resource = this.jabberService.localRDF.getResource(resource);
        resource = resorce.getTarget("avatar-uri", JIM_NAMESPACE_URI+"rdf/avatars#");

        return resource && resource.value;
    },

    getAvatarByResourceId: function(resource)
    {
        if (!resource)
            return null;

        resource = this.jabberService.localRDF.getResource(resource);
        resource = resource.getTarget("avatar-uri", JIM_NAMESPACE_URI+"rdf/avatars#");

        return resource && resource.value;
    },

    getThisAvatar: function()
    {
        return this.avatarUri;
    },

    setThisAvatar: function(uri)
    {
        var file, data, encodedData, hash, tag;

        this.jabberService.localRDF.ns = JIM_NAMESPACE_URI+"rdf/avatars#";

        if (this.avatarUri == uri)
            return;

        if (!uri) {
            this.avatarUri = this.avatarHash = null;
            this.jabberService.core.iq.avatar.encodedImage = "";
            this.jabberService.core.highlevel.users.thisUser.
                removeAssertion("avatar-uri");

            tag = DB.createElement("storage:client:avatar", "data");
            this.jabberService.core.iq.privat.setPrivate(tag);

            this.jabberService.core.highlevel.users.sendPresence();
            return;
        }

        try {
            file = new File(uri);
        } catch (ex) {
            file = new Reader(uri);
        }

        if (file instanceof File) {
            if (!file.exists)
                throw "Plik nie istnieje";
            file.open(file.MODE_RDONLY);
        } else
            file.open();

        if (file.available > 8192)
            throw "Plik jest zbyt duży";

        data = file.read();
        file.close();

        if (this.avatarsUriHash[uri])
            hash = this.avatarsUriHash[uri];
        else
            hash = sha1(data);

        this.avatarHash = hash;
        this.jabberService.core.iq.avatar.encodedImage = stringToBase64(data);

        tag = DB.createElement("storage:client:avatar", "data");
        tag.addElement("storage:client:avatar", "data").
            addText(this.jabberService.core.iq.avatar.encodedImage);

        this.jabberService.core.iq.privat.setPrivate(tag);

        if (this.avatarsHash[hash])
            this.avatarUri = this.avatarsHash[hash];
        else
            this.avatarUri = this.addImageToCache(data, hash);

        this.jabberService.core.highlevel.users.thisUser.
            setAssertion("avatar-uri", this.avatarUri, true);

        this.fireEvent("onAvatarThisChanged",
            this.jabberService.core.highlevel.users.thisUser);

        this.jabberService.core.highlevel.users.sendPresence();
    },

    setAvatarsHash: function(hash, uri)
    {
        var res = this.rdf.getResource();

        this.avatarsHash[hash] = uri;
        this.avatarsUriHash[uri] = hash;

        res.setAssertion("url", uri, true);
        res.setAssertion("hash", hash, true);
        this.rdf.getContainer("rdf:jim-avatars").
            appendElement(res);

        var res = this.jabberService.localRDF.getResource();
        res.setAssertion("url", uri, true, JIM_NAMESPACE_URI+"rdf/avatars#");
        this.jabberService.localRDF.getContainer("rdf:jim-avatars", "seq").
            appendElement(res);
    },

    onIQAvatar: function(from, data)
    {
        var hash, url;
        var resource = this.jabberService.core.highlevel.users.
            getResourceIDByLongJID(from);

        if (!resource)
            return;

        setTimeout(this.__addImageToCache, 0, this, from, data,
            this.jabberService.localRDF.getResource(resource));
    },

    __addImageToCache: function(_this, from, data, resource)
    {
        var hash, url;
        var rawdata = base64ToString(data);

        if (!rawdata.length) {
            if (from == _this.jabberService.core.uidShort) {
                _this.jabberService.localRDF.ns = JIM_NAMESPACE_URI+"rdf/avatars#";
                _this.avatarUri = _this.avatarHash = null;
                _this.jabberService.core.iq.avatar.encodedImage = "";
                _this.jabberService.core.highlevel.users.thisUser.
                    removeAssertion("avatar-uri");
            }
            return;
        }

        hash = sha1(rawdata);
        url = hash in _this.avatarsHash ? _this.avatarsHash[hash] : null;

        if (!url)
            url = _this.addImageToCache(rawdata, hash);

        resource.setAssertion("avatar-uri", url, true, JIM_NAMESPACE_URI+"rdf/avatars#");

        if (from == _this.jabberService.core.uidShort) {
            _this.jabberService.core.iq.avatar.encodedImage = data;
            _this.avatarHash = hash;
            _this.avatarUri = url;
            _this.jabberService.core.highlevel.users.thisUser.
                setAssertion("avatar-uri", url, true);
            _this.fireEvent("onAvatarThisChanged",
                _this.jabberService.core.highlevel.users.thisUser);
            _this.jabberService.core.highlevel.users.sendPresence();
        } else
            _this.fireEvent("onAvatarChanged",
                _this.jabberService.core.highlevel.users.users,
                resource.getTarget("resource-user", JIM_NAMESPACE_URI+"rdf/users#").value, resource.value);
    },

    addFileToCache: function(reader)
    {
        var data;

        if (reader instanceof File) {
            if (!reader.exists)
                throw "Plik nie istnieje";
            reader.open(reader.MODE_RDONLY);
        } else
            reader.open();

        if (reader.available > 8192)
            throw "Plik jest zbyt duży";

        data = reader.read();
        reader.close();
        return this.addImageToCache(data, sha1(data));
    },

    addImageToCache: function(data, hash)
    {
        var file;

        if (hash in this.avatarsHash)
            return this.avatarsHash[hash];

        do {
            file = new File(this.avatarsDir, generateRandomName());
        } while (file.exists);

        file.open(file.MODE_WRONLY | file.MODE_CREATE);
        file.write(data);
        file.close();

        this.setAvatarsHash(hash, file.uri.spec);
        return file.uri.spec;
    },

    flushImageCache: function()
    {
        var container = this.rdf.getContainer("rdf:jim-avatars");
        var nodes = container.getElements();

        while (nodes.hasNext()) {
            node = nodes.getNext();
            try {
                var f = new File(node.getTarget("url").value);
                f.deleteFile();
            } catch (ex) { }
        }
        container.removeElements(true)
    },

    onConnect: function(tag)
    {
        this.jabberService.core.iq.privat.getPrivate("storage:client:avatar");
    },

    onClean: function()
    {
        this.jabberService.localRDF.ns = JIM_NAMESPACE_URI+"rdf/avatars#";
        this.avatarUri = this.avatarHash = null;
        this.jabberService.core.iq.avatar.encodedImage = "";
        this.jabberService.core.highlevel.users.thisUser.
            removeAssertion("avatar-uri");
    },

    onDisconnect: function(tag)
    {
        this.onClean();
    },

    onIQPrivate: function(tag)
    {
        setTimeout(this.__addImageToCache, 0, this,
            this.jabberService.core.uidShort, domToText(tag),
            this.jabberService.core.highlevel.users.thisUser);
    },

    onPresence: function(tag)
    {
        var t = tag.getElementsByTagNameNS('jabber:x:avatar', 'x')[0];
        var hash, val;

        if (t && (t = t.getElementsByTagName("hash")[0])) {
            var from = tag.getAttribute('from');
            var resource = this.jabberService.core.highlevel.users.
                getResourceIDByLongJID(from);

            if (!resource)
                return;

            hash = domToText(t);

            resource = this.jabberService.localRDF.getResource(resource);
            this.jabberService.localRDF.ns = JIM_NAMESPACE_URI+"rdf/avatars#";

            val = resource.getTarget("avatar-uri");
            if (val && val.value == this.avatarsHash[hash])
                return;

            if (t = this.avatarsHash[hash]) {
                resource.setAssertion("avatar-uri", t, true);
            } else {
                resource.avatar = '';
                if (this.avatarsHash[hash] != '')
                    this.jabberService.core.iq.avatar.getAvatar(from);
                resource.setAssertion("avatar-uri", '', true);
            }
            this.fireEvent("onAvatarChanged",
                this.jabberService.core.highlevel.users.users,
                resource.getTarget("resource-user", JIM_NAMESPACE_URI+"rdf/users#").value,
                resource.value);
        }
    },

    onPresenceSend: function(tag)
    {
        if (this.avatarUri)
            tag.addElement("jabber:x:avatar", "x").
                addElement("jabber:x:avatar", "hash").
                    addText(this.avatarHash);
    },
}
