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

/**
 * rdf.RDF
 *
 *
 */
ML.load("utils/misc.js");
ML.load("utils/debug.js");

const JIM_ERROR_RDF_BAD_ARGS          = 0x70000008;

function RDF(namespaceURI, dataSource)
{
    this.ns = namespaceURI
    this.RDF = Components.classes['@mozilla.org/rdf/rdf-service;1'].
         getService(Components.interfaces.nsIRDFService);
    this.RDFC = Components.classes["@mozilla.org/rdf/container;1"].
        getService(Components.interfaces.nsIRDFContainer);
    this.RDFCU = Components.classes["@mozilla.org/rdf/container-utils;1"].
        getService(Components.interfaces.nsIRDFContainerUtils);
    this.DS = dataSource;
}

RDF.prototype =
{
    RDFNode: RDFNode,
    RDFIterator: RDFIterator,
    RDFLiteral: RDFLiteral,
    RDFResource: RDFResource,
    RDFContainer: RDFContainer,

    decorate: function(node)
    {
        if (!node)
            return null;

        try {
            node = node.QueryInterface(Components.interfaces.nsIRDFLiteral);
            return this.getLiteral(node);
        } catch(e) {
            try {
                node = node.QueryInterface(Components.interfaces.nsIRDFResource);

                if (this.RDFCU.IsContainer(this.DS, node))
                    return this.getContainer(node);
                return this.getResource(node);
            } catch (ex) {
                return null;
            }
        } 
    },

    getResource: function(uri)
    {
        if (!uri)
            uri = "rdf:"+generateRandomName();
        if (typeof(uri) == "string") {
            return  new RDFResource(this, uri);
        } else {
            try {
                uri = uri.QueryInterface(Components.interfaces.nsIRDFResource);

                return new this.RDFResource(this, uri);
            } catch (ex) {
                return null;
            }
        }
    },

    getContainer: function(uri, type)
    {
        var res, obj;

        if (!uri)
            uri = "rdf:"+generateRandomName();

        if (typeof(uri) == "string")
            obj = uri;
        else {
            try {
                obj = uri.QueryInterface(Components.interfaces.nsIRDFResource);
                uri = obj.Value;
            } catch (ex) {
                return null;
            }
        }

        res = new this.RDFContainer(this, obj);

        if (!this.RDFCU.IsContainer(this.DS, res.node)) {
            switch (type) {
                case "bag":
                    this.RDFCU.MakeBag(this.DS, res.node);
                    return res;
                case "seq":
                    this.RDFCU.MakeSeq(this.DS, res.node);
                    return res;
                case "alt":
                    this.RDFCU.MakeAlt(this.DS, res.node);
                    return res;
                default:
                    return null;
            }
        }
        return res;
    },

    getLiteral: function(value)
    {
        var val;

        if (typeof(value) == "string") {
            val = value;
            value = this.RDF.GetLiteral(value);
        } else
            val = value.Value;

        return new this.RDFLiteral(this, value);
    },

    getIterator: function(it)
    {
        return new this.RDFIterator(this, it);
    }
}


function RDFIterator(rdf, enumeration)
{
    this.rdf = rdf;
    this.enumeration = enumeration;
}

RDFIterator.prototype =
{
    hasNext: function()
    {
        if (this.enumeration)
            return this.enumeration.hasMoreElements();
        return false;
    },

    getNext: function()
    {
        return this.rdf.decorate(this.enumeration.getNext());
    }
}

function RDFNode(rdf)
{
    this.rdf = rdf;
}

RDFNode.prototype =
{
    TYPE_RESOURCE: 1,
    TYPE_CONTAINER: 2,
    TYPE_LITERAL: 3,

    __argResource: function(arg, ns)
    {
        if (typeof(arg) != "object")
            return this.rdf.RDF.GetResource((ns || this.rdf.ns) + arg);
        else if ("type" in arg && arg.type != 3)
            return arg.node;
        else if ("type" in arg)
            throw { code: JIM_ERROR_RDF_BAD_ARGS };
        return arg;
    },

    __argRDFResource: function(arg, ns)
    {
        if (typeof(arg) != "object")
            return this.rdf.getResource((ns || this.rdf.ns) + arg);
        else if ("type" in arg && arg.type != 3)
            return arg;
        else if ("type" in arg)
            throw { code: JIM_ERROR_RDF_BAD_ARGS };
        return this.rdf.decorate(arg);
    },

    __argNode: function(arg, asLiteral)
    {
        if (typeof(arg) != "object")
            if (asLiteral)
                return this.rdf.RDF.GetLiteral(arg);
            else
                return this.rdf.RDF.GetResource(arg);
        else if ("type" in arg)
            return arg.node;
        return arg;
    },

    __argRDFNode: function(arg, asLiteral)
    {
        if (typeof(arg) != "object")
            if (asLiteral)
                return this.rdf.getLiteral(arg);
            else
                return this.rdf.getResource(arg);
        else if ("type" in arg)
            return arg;
        return this.rdf.decorate(arg);
    },
}

function RDFResource(rdf, uri)
{
    this.base = rdf.RDFNode;
    this.base(rdf);

    if (typeof(uri) == "string") {
        this.value = uri;
        this.node = rdf.RDF.GetResource(uri);
    } else {
        this.value = uri.Value;
        this.node = uri;
    }
    this.type = this.TYPE_RESOURCE;
}

RDFResource.prototype =
{
    __proto__: RDFNode.prototype,

    setAssertion: function(property, object, asLiteral, ns)
    {
        var old;

        if (!property || !object)
            return;

        property = this.__argResource(property, ns);
        object = this.__argNode(object, asLiteral);

        old = this.rdf.DS.GetTarget(this.node, property, true);

        if (old)
            this.rdf.DS.Change(this.node, property, old, object);
        else
            this.rdf.DS.Assert(this.node, property, object, true);
    },

    addAssertion: function(property, object, ns)
    {
        if (!property || !object)
            return;

        property = this.__argResource(property, ns);
        object = this.__argNode(object, asLiteral);

        this.rdf.DS.Assert(this.node, property, object, true);
    },

    hasAssertion: function(property, object, asLiteral, ns)
    {
        if (!property || !object)
            return false;

        property = this.__argResource(property, ns);
        object = this.__argNode(object, asLiteral);

        return this.rdf.DS.HasAssertion(this.node, property, object, true);
    },

    hasAnyAssertion: function(property, ns)
    {
        if (!property)
            return false;

        property = this.__argResource(property, ns);

        return this.rdf.DS.GetTarget(this.node, property, true) != null;
    },

    getArcLabelsIn: function()
    {
        return this.rdf.getIterator(this.rdf.DS.ArcLabelsIn(this.node));
    },

    getArcLabelsOut: function()
    {
        return this.rdf.getIterator(this.rdf.DS.ArcLabelsOut(this.node));
    },

    getTarget: function(property, ns)
    {
        if (!property)
            return null;

        property = this.__argResource(property, ns);

        return this.rdf.decorate(this.rdf.DS.GetTarget(this.node, property, true));
    },

    getTargets: function(property, ns)
    {
        if (!property)
            return null;

        property = this.__argResource(property, ns);

        return this.rdf.getIterator(this.rdf.DS.GetTargets(this.node, property, true));
    },


    getSource: function(property, ns)
    {
        var iter;

        if (!property)
            return null;

        property = this.__argResource(property, ns);

        iter = this.rdf.DS.GetSources(property, this.node, true);
        if (iter.hasMoreElements())
            return this.rdf.decorate(iter.getNext());

        return null;
    },

    getSources: function(property, ns)
    {
        if (!property)
            return null;

        property = this.__argResource(property, ns);

        return this.rdf.getIterator(this.rdf.DS.GetSources(property, this.node, true));
    },

    removeAssertion: function(property, object, depth, asLiteral, ns)
    {
        var val;

        if (!property)
            return;

        property = this.__argResource(property, ns);

        if (!object)
            object = this.rdf.DS.GetTarget(this.node, property, true);
        else
            object = this.__argNode(object, asLiteral);

        if (object) {
            this.rdf.DS.Unassert(this.node, property, object);

            if (depth)
                this.rdf.decorate(object).remove(true);
        }
    },

    removeAssertions: function(property, depth, ns)
    {
        var object, iter;

        if (!property)
            return;

        property = this.__argResource(property, ns);

        iter = this.getTargets(property);
        while (iter.hasNext()) {
            object = iter.getNext();
            this.removeAssertion(property, object, depth);

            if (depth)
                object.remove(true);
        }
    },

    remove: function(depth)
    {
        var iter, iter2, prop;

        iter = this.getArcLabelsIn();

        while (iter.hasNext()) {
            prop = iter.getNext();

            iter2 = this.getSources(prop);
            while (iter2.hasNext())
                iter2.getNext().removeAssertion(prop, this);
        }

        iter = this.getArcLabelsOut();

        while (iter.hasNext())
            this.removeAssertions(iter.getNext(), depth)
    }
}

function RDFContainer(rdf, uri)
{
    this.base = rdf.RDFResource;
    this.base(rdf, uri);
    this.type = this.TYPE_CONTAINER;
}

RDFContainer.prototype =
{
    __proto__: RDFResource.prototype,

    isEmpty: function()
    {
        return this.rdf.RDFCU.IsEmpty(this.rdf.DS, this.node);
    },

    getCount: function()
    {
        this.rdf.RDFC.Init(this.rdf.DS, this.node);
        return this.rdf.RDFC.GetCount();
    },

    getElements: function()
    {
        try {
            this.rdf.RDFC.Init(this.rdf.DS, this.node);
            return this.rdf.getIterator(this.rdf.RDFC.GetElements());
        } catch (ex) {
            return this.rdf.getIterator(null);
        }
    },

    appendElement: function(element, asLiteral)
    {
        if (!element)
            return;

        element = this.__argNode(element, asLiteral);

        this.rdf.RDFC.Init(this.rdf.DS, this.node);
        this.rdf.RDFC.AppendElement(element);
    },

    removeElement: function(element, depth, asLiteral)
    {
        if (!element)
            return;

        element = this.__argRDFNode(element, asLiteral);

        try {
            this.rdf.RDFC.Init(this.rdf.DS, this.node);
            this.rdf.RDFC.RemoveElement(element.node, true);

            if (depth)
                element.remove(true);
        } catch (ex) { }
    },

    removeElements: function(depth)
    {
        var it = this.getElements(), node;

        while (it.hasNext())
            it.getNext().remove(depth);
//            this.removeElement(it.getNext(), depth);
    },

    insertElementAt: function(element, index, asLiteral)
    {
        if (!element || !index || index < 0)
            return;

        element = this.__argNode(element, asLiteral);

        this.rdf.RDFC.Init(this.rdf.DS, this.node);
        this.rdf.RDFC.InsertElementAt(element, index);
    },

    removeElementAt: function(element, ns, index, depth, asLiteral)
    {
        if (!element || !index || index < 0)
            return;

        element = this.__argRDFNode(element, asLiteral);

        this.rdf.RDFC.Init(this.rdf.DS, this.node);
        this.rdf.RDFC.RemoveElementAt(element.node, index, true);

        if (depth)
            element.remove(true);
    },

    indexOf: function(element, asLiteral, ns)
    {
        if (!element)
            return -1;

        element = this.__argNode(element, asLiteral);

        this.rdf.RDFC.Init(this.rdf.DS, this.node);
        return this.rdf.RDFC.IndexOf(element);
    },

    hasElement: function(element, asLiteral, ns)
    {
        return this.indexOf(element, asLiteral, ns) != -1;
    },

    remove: function(depth)
    {
        var iter, iter2, prop;

        this.removeElements(depth);

        iter = this.getArcLabelsIn();

        while (iter.hasNext()) {
            prop = iter.getNext();

            iter2 = this.getSources(prop);
            while (iter2.hasNext())
                iter2.getNext().removeAssertion(prop, this);
        }

        if (depth) {
            iter = this.getArcLabelsOut();

            while (iter.hasNext())
                this.removeAssertions(iter.getNext(), depth)
        }        
    }
}

function RDFLiteral(rdf, value)
{
    this.base = rdf.RDFNode;
    this.base(rdf);

    if (typeof(value) == "string") {
        this.value = value;
        this.node = rdf.RDF.GetLiteral(value);
    } else {
        this.value = value.Value;
        this.node = value;
    }
    this.type = this.TYPE_LITERAL;
}

RDFLiteral.prototype =
{
    __proto__: RDFNode.prototype,

    getSource: function(property, ns)
    {
        var iter;

        if (!property)
            return;

        property = this.__argResource(property, ns);

        iter = this.rdf.DS.GetSources(property, this.node, true);
        if (iter.hasMoreElements())
            return this.rdf.decorate(iter.getNext());

        return null;
    },

    getSources: function(property, ns)
    {
        if (!property)
            return;

        property = this.__argResource(property, ns);

        return this.rdf.getIterator(this.rdf.DS.GetSources(property, this.node, true));
    },

    getArcLabelsIn: function()
    {
        return this.rdf.getIterator(this.rdf.DS.ArcLabelsIn(this.node));
    },

    remove: function()
    {
    }
}