/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.clerezza.rdf.jena.facade;

import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.TripleMatch;
import com.hp.hpl.jena.graph.impl.GraphBase;
import com.hp.hpl.jena.mem.TrackingTripleIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;

import org.apache.clerezza.rdf.core.BNode;
import org.apache.clerezza.rdf.core.NonLiteral;
import org.apache.clerezza.rdf.core.Resource;
import org.apache.clerezza.rdf.core.Triple;
import org.apache.clerezza.rdf.core.TripleCollection;
import org.apache.clerezza.rdf.core.UriRef;
import org.apache.clerezza.rdf.jena.commons.Jena2TriaUtil;
import org.apache.clerezza.rdf.jena.commons.Tria2JenaUtil;
import org.wymiwyg.commons.util.collections.BidiMap;
import org.wymiwyg.commons.util.collections.BidiMapImpl;

/**
 * This class implements {@link com.hp.hpl.jena.graph.Graph} basing
 * on a {@link org.apache.clerezza.rdf.core.TripleCollection}. A <code>JenaGraph</code>
 * can be instanciated using mutable <code>TripleCollection</code>s
 * (i.e. <code>MGraph</code>S) as well as immutable ones (i.e. <code>Graph</code>),
 * an attempt to add or remove triples to a <code>JenaGraph</code> based on
 * an immutable <code>TripleCollection</code> will result in
 * a <code>UnsupportedOperationException</code> being thrown by the
 * underlying <code>TripleCollection</code>.
 *
 * Typically an instance of this class is passed as argument
 * to {@link com.hp.hpl.jena.rdf.model.ModelFactory#createModelForGraph} to
 * get a <code>Model</code>.
 *
 * @author reto
 */
public class JenaGraph extends GraphBase implements Graph {



    final TripleCollection graph;
    final BidiMap<BNode, Node> tria2JenaBNodes = new BidiMapImpl<BNode, Node>();
    final Jena2TriaUtil jena2TriaUtil =
            new Jena2TriaUtil(tria2JenaBNodes.inverse());
    final Tria2JenaUtil tria2JenaUtil =
            new Tria2JenaUtil(tria2JenaBNodes);

    public JenaGraph(TripleCollection graph) {
        this.graph = graph;
    }

    @Override
    public void performAdd(com.hp.hpl.jena.graph.Triple triple) {
        graph.add(jena2TriaUtil.convertTriple(triple));
    }

    @Override
    public void performDelete(com.hp.hpl.jena.graph.Triple triple) {
        Triple clerezzaTriple = jena2TriaUtil.convertTriple(triple);
        if (clerezzaTriple != null) {
            graph.remove(clerezzaTriple);
        }
    }

    private Iterator<com.hp.hpl.jena.graph.Triple> convert(
            final Iterator<Triple> base) {
        return new Iterator<com.hp.hpl.jena.graph.Triple>() {

            Triple lastReturned = null;

            @Override
            public boolean hasNext() {
                return base.hasNext();
            }

            @Override
            public com.hp.hpl.jena.graph.Triple next() {
                Triple baseNext = base.next();
                lastReturned = baseNext;
                return (baseNext == null) ? null : tria2JenaUtil.convertTriple(baseNext, true);
            }

            @Override
            public void remove() {
                graph.remove(lastReturned);
            }
        };
    }

    /**
     * An iterator (over a filtered TripleCollection) that can return its next element as a triple. As parameter a tripleMatch is required.
     * Triple matches are defined by subject, predicate, and object.
     * @param m
     * @return TripleCollection
     */
    private Iterator<Triple> filter(TripleMatch m) {
        NonLiteral subject = null;
        UriRef predicate = null;
        Resource object = null;
        if (m.getMatchSubject() != null) {
            if (m.getMatchSubject().isLiteral()) {
                return Collections.EMPTY_SET.iterator();
            }
            subject = jena2TriaUtil.convertNonLiteral(m.getMatchSubject());
            if (subject == null) {
                return Collections.EMPTY_SET.iterator();
            }
        }
        if (m.getMatchObject() != null) {
            object = jena2TriaUtil.convertJenaNode2Resource(m.getMatchObject());
            if (object == null) {
                return Collections.EMPTY_SET.iterator();
            }
        }        
        if (m.getMatchPredicate() != null) {
            if (!m.getMatchPredicate().isURI()) {
                return Collections.EMPTY_SET.iterator();
            }
            predicate = jena2TriaUtil.convertJenaUri2UriRef(m.getMatchPredicate());
        }

        try {
            return graph.filter(subject, predicate, object);
        } catch (IllegalArgumentException e) {
            //jena serializers are known to query with invalid URIs
            //see http://tech.groups.yahoo.com/group/jena-dev/message/37221
            //an invalid Uris hould not be in the graph and thus lead to an
            //empty result
            return new HashSet<Triple>().iterator();
        }
    }

    @Override
    protected ExtendedIterator graphBaseFind(TripleMatch m) {
        return new TrackingTripleIterator(convert(filter(m)));
    }
}
