/*
 * 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.jena.rdf.model.impl;

import java.util.Iterator;

import org.apache.jena.graph.* ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.reasoner.* ;

/**
 * Default implementation of the InfModel interface which simply wraps up
 * an InfGraph.
 */
public class InfModelImpl extends ModelCom implements InfModel {

    /**
     * Constructor.
     * @param infgraph the InfGraph, as generated by a reasoner.bind operation, to be packaged up.
     */
    public InfModelImpl(InfGraph infgraph) {
        super(infgraph);
    }

    /**
     * Return the underlying inference graph for this model.
     */
    public InfGraph getInfGraph() {
        return (InfGraph)getGraph();
    }
    
    /**
     * Return the raw RDF model being processed (i.e. the argument
     * to the Reasoner.bind call that created this InfModel).
     */
    @Override
    public Model getRawModel() {
        return new ModelCom(getInfGraph().getRawGraph());
    }
    
    /**
     * Return the Reasoner which is being used to answer queries to this graph.
     */
    @Override
    public Reasoner getReasoner() {
        return getInfGraph().getReasoner();
    }

    /**
     * Cause the inference model  to reconsult the underlying data to take
     * into account changes. Normally changes are made through the InfModel's add and
     * remove calls are will be handled appropriately. However, in some cases changes
     * are made "behind the InfModels's back" and this forces a full reconsult of
     * the changed data. 
     */
    @Override
    public void rebind() {
        getInfGraph().rebind();
    }
    
    /**
     * Perform any initial processing and caching. This call is optional. Most
     * engines either have negligable set up work or will perform an implicit
     * "prepare" if necessary. The call is provided for those occasions where
     * substantial preparation work is possible (e.g. running a forward chaining
     * rule system) and where an application might wish greater control over when
     * this preparation is done rather than just leaving to be done at first query time.
     */
    @Override
    public void prepare() {
        getInfGraph().prepare();
    }
    
    /**
     * Reset any internal caches. Some systems, such as the tabled backchainer, 
     * retain information after each query. A reset will wipe this information preventing
     * unbounded memory use at the expense of more expensive future queries. A reset
     * does not cause the raw data to be reconsulted and so is less expensive than a rebind.
     */
    @Override
    public void reset() {
        getInfGraph().reset();
    }
    
    /**
     * Test the consistency of the underlying data. This normally tests
     * the validity of the bound instance data against the bound
     * schema data. 
     * @return a ValidityReport structure
     */
    @Override
    public ValidityReport validate() {
        return getInfGraph().validate();
    }
    
    /** Find all the statements matching a pattern.
     * <p>Return an iterator over all the statements in a model
     *  that match a pattern.  The statements selected are those
     *  whose subject matches the <code>subject</code> argument,
     *  whose predicate matches the <code>predicate</code> argument
     *  and whose object matchesthe <code>object</code> argument.
     *  If an argument is <code>null</code> it matches anything.</p>
     * <p>
     * The s/p/o terms may refer to resources which are temporarily defined in the "posit" model.
     * This allows one, for example, to query what resources are of type CE where CE is a
     * class expression rather than a named class - put CE in the posit arg.</p>
     * 
     * @return an iterator over the subjects
     * @param subject   The subject sought
     * @param predicate The predicate sought
     * @param object    The value sought
     */ 
    @Override
    public StmtIterator listStatements( Resource subject, Property predicate, RDFNode object, Model posit ) {
        Iterator<Triple> iter = getInfGraph().find(asNode(subject), asNode(predicate), asNode(object), posit.getGraph());
        return IteratorFactory.asStmtIterator(iter,this);
    }
    
    /**
     * Switch on/off drivation logging. If this is switched on then every time an inference
     * is a made that fact is recorded and the resulting record can be access through a later
     * getDerivation call. This may consume a lot of space!
     */
    @Override
    public void setDerivationLogging(boolean logOn) {
        getInfGraph().setDerivationLogging(logOn);
    }
   
    /**
     * Return the derivation of the given statement (which should be the result of
     * some previous list operation).
     * Not all reasoneers will support derivations.
     * @return an iterator over Derivation records or null if there is no derivation information
     * available for this triple.
     */
    @Override
    public Iterator<Derivation> getDerivation(Statement statement) {
        return getInfGraph().getDerivation(statement.asTriple());
    }
    
    /**
     * Returns a derivations model. The rule reasoners typically create a 
     * graph containing those triples added to the base graph due to rule firings.
     * In some applications it can useful to be able to access those deductions
     * directly, without seeing the raw data which triggered them. In particular,
     * this allows the forward rules to be used as if they were rewrite transformation
     * rules.
     * @return the deductions model, if relevant for this class of inference
     * engine or null if not.
     */
    @Override
    public Model getDeductionsModel() {
        Graph deductionsGraph = getInfGraph().getDeductionsGraph();
        if (deductionsGraph != null) {
            if (deductionsModel == null || (deductionsModel.getGraph() != deductionsGraph) ) {
                deductionsModel = new ModelCom(deductionsGraph);
            }
        }
        return deductionsModel;
    }
    
    /** Cached deductions model */
    private Model deductionsModel = null;
}
