/*
* 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.stanbol.entityhub.ldpath;

import java.util.Collection;

import org.apache.stanbol.entityhub.core.mapping.ValueConverterFactory.AnyUriConverter;
import org.apache.stanbol.entityhub.core.mapping.ValueConverterFactory.ReferenceConverter;
import org.apache.stanbol.entityhub.core.mapping.ValueConverterFactory.TextConverter;
import org.apache.stanbol.entityhub.core.mapping.ValueConverterFactory.ValueConverter;
import org.apache.stanbol.entityhub.core.model.InMemoryValueFactory;
import org.apache.stanbol.entityhub.ldpath.transformer.ValueConverterTransformerAdapter;
import org.apache.stanbol.entityhub.servicesapi.defaults.DataTypeEnum;
import org.apache.stanbol.entityhub.servicesapi.defaults.NamespaceEnum;
import org.apache.stanbol.entityhub.servicesapi.model.Reference;
import org.apache.stanbol.entityhub.servicesapi.model.Representation;
import org.apache.stanbol.entityhub.servicesapi.model.Text;
import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory;

import at.newmedialab.ldpath.LDPath;
import at.newmedialab.ldpath.api.backend.RDFBackend;
import at.newmedialab.ldpath.api.transformers.NodeTransformer;
import at.newmedialab.ldpath.model.fields.FieldMapping;
import at.newmedialab.ldpath.model.programs.Program;
import at.newmedialab.ldpath.parser.DefaultConfiguration;

/**
 * {@link LDPath} with Entityhub specific configurations.
 * In detail this registers {@link NodeTransformer} for:<ul>
 * <li> {@link DataTypeEnum#Reference} returning {@link Reference} instances
 * <li> xsd:anyURI also returning {@link Reference} instances
 * <li> {@link DataTypeEnum#Text} returning {@link Text} instances
 * <li> xsd:string also returning {@link Text} instances
 * </ul><p>
 * It adds also support for returning {@link Representation} instances as
 * result of executing {@link Program}s on a context. This is important because
 * it allows a seamless integration of LDPath with existing Entityhub
 * functionality/interfaces
 * 
 * <p>Because there is currently
 * no way to get the LDPath parser to instantiate an extension of {@link Program}
 * this feature is currently implemented by {@link #execute(Reference, Program)}
 * of this class.
 * 
 * @author Rupert Westenthaler
 *
 */
public class EntityhubLDPath extends LDPath<Object> {

    private final ValueFactory vf;
    private final RDFBackend<Object> backend;
    /**
     * Creates a {@link LDPath} instance configured as used with the Entityhub.
     * This means support for<ul>
     * <li> namespaces defined by the {@link NamespaceEnum}
     * <li> {@link NodeTransformer} for {@link DataTypeEnum#Text} and 
     * {@link DataTypeEnum#Reference}
     * <li> and the usage of {@link Text} for <code>xsd:string</code> and
     * {@link Reference} for <code>xsd:anyURI</code>
     * @param backend the {@link RDFBackend}
     */
    public EntityhubLDPath(RDFBackend<Object> backend) {
        this(backend,null);
    }
    /**
     * Creates a {@link LDPath} instance configured as used with the Entityhub.
     * This means support for<ul>
     * <li> namespaces defined by the {@link NamespaceEnum}
     * <li> {@link NodeTransformer} for {@link DataTypeEnum#Text} and 
     * {@link DataTypeEnum#Reference}
     * <li> and the usage of {@link Text} for <code>xsd:string</code> and
     * {@link Reference} for <code>xsd:anyURI</code>
     * @param backend the {@link RDFBackend}
     * @param vf the {@link ValueFactory} or <code>null</code> to use the default.
     */
    public EntityhubLDPath(RDFBackend<Object> backend,ValueFactory vf) {
        super(backend, new EntityhubConfiguration(vf));
        this.vf = vf == null ? InMemoryValueFactory.getInstance() : vf;
        this.backend = backend;
    }
    
    /**
     * Executes the parsed {@link Program} and stores the 
     * {@link Program#getFields() fields} in a {@link Representation}. The actual
     * implementation used for the {@link Representation} depends on the
     * {@link ValueFactory} of this EntityhubLDPath instance
     * @param context the context
     * @param program the program 
     * @return the {@link Representation} holding the results of the execution
     * @throws IllegalArgumentException if the parsed context or the program is
     * <code>null</code>
     */
    public Representation execute(Reference context,Program<Object> program){
        if(context == null){
            throw new IllegalArgumentException("The parsed context MUST NOT be NULL!");
        }
        if(program == null){
            throw new IllegalArgumentException("The parsed program MUST NOT be NULL!");
        }
        Representation result = vf.createRepresentation(context.getReference());
        for(FieldMapping<?,Object> mapping : program.getFields()) {
            Collection<?> values = mapping.getValues(backend,context);
            if(values !=null && !values.isEmpty()){
                result.add(mapping.getFieldName(),values);
            }
        }
        return result;
        
    }
    /**
     * The default configuration for the Entityhub
     * @author Rupert Westenthaler
     *
     */
    public static class EntityhubConfiguration extends DefaultConfiguration<Object>{
        public EntityhubConfiguration(ValueFactory vf){
            super();
            vf = vf == null ? InMemoryValueFactory.getInstance() : vf;
            //register special Entutyhub Transformer for
            // * entityhub:reference
            ValueConverter<Reference> referenceConverter = new ReferenceConverter();
            addTransformer(referenceConverter.getDataType(), 
                new ValueConverterTransformerAdapter<Reference>(referenceConverter,vf));
            // * xsd:anyURI
            ValueConverter<Reference> uriConverter = new AnyUriConverter();
            addTransformer(uriConverter.getDataType(), 
                new ValueConverterTransformerAdapter<Reference>(uriConverter,vf));
            // * entityhub:text
            ValueConverter<Text> literalConverter = new TextConverter();
            addTransformer(literalConverter.getDataType(), 
                new ValueConverterTransformerAdapter<Text>(literalConverter,vf));
            // xsd:string (use also the literal converter for xsd:string
            addTransformer(DataTypeEnum.String.getUri(), 
                new ValueConverterTransformerAdapter<Text>(literalConverter,vf));
            //Register the default namespaces
            for(NamespaceEnum ns : NamespaceEnum.values()){
                addNamespace(ns.getPrefix(), ns.getNamespace());
            }
        }
    }
}
