/*
* 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.indexing.source.jenatdb;

import java.net.URI;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ThreadPoolExecutor;

import com.hp.hpl.jena.datatypes.TypeMapper;
import com.hp.hpl.jena.datatypes.xsd.XSDDateTime;
import com.hp.hpl.jena.graph.Node;

import at.newmedialab.ldpath.api.backend.RDFBackend;
import at.newmedialab.ldpath.model.backend.AbstractBackend;

/**
 * Implements all the value converter methods of {@link RDFBackend}.
 * @author Rupert Westenthaler
 *
 */
public abstract class AbstractTdbBackend extends AbstractBackend<Node> implements RDFBackend<Node> {

    /**
     * Avoids massive instance creation for language literal
     */
    private Map<String,Locale> localeCache = new TreeMap<String,Locale>();
    /**
     * Avoids massive instance creation for typed literal URIs
     */
    private Map<String,URI> xsdTypeCache = new TreeMap<String,URI>();
    /**
     * Provides the jena type for a type URI
     */
    private TypeMapper typeMapper = TypeMapper.getInstance();
    
    private Locale toLocale(String lang){
        if(lang == null){ 
            return null;
        }
        Locale locale = localeCache.get(lang);
        if(locale == null){
            locale = new Locale(lang);
            localeCache.put(lang, locale);
        }
        return locale;
    }
    private URI toLiteralTypeURI(String type){
        if(type == null){ 
            return null;
        }
        URI uri = xsdTypeCache.get(type);
        if(uri == null){
            uri = URI.create(type);
            xsdTypeCache.put(type, uri);
        }
        return uri;
    }    
    
    @Override
    public boolean supportsThreading() {
        //threading is supposed to be used in cases where LDpath needs to
        //use remote services. No advantage of having multiple threads on a
        //triple store limited by local File access.
        return false;
    }

    @Override
    public ThreadPoolExecutor getThreadPool() {
        return null;
    }

    @Override
    public boolean isLiteral(Node n) {
        return n.isLiteral();
    }

    @Override
    public boolean isURI(Node n) {
        return n.isURI();
    }

    @Override
    public boolean isBlank(Node n) {
        return n.isBlank();
    }


    @Override
    public Locale getLiteralLanguage(Node n) {
        if(n.isLiteral()){
            return toLocale(n.getLiteralLanguage());
        } else {
            throw new IllegalArgumentException("The parsed Node is not a Literal");
        }
    }


    @Override
    public URI getLiteralType(Node n) {
        if(n.isLiteral()){
            return toLiteralTypeURI(n.getLiteralDatatypeURI());
        } else {
            throw new IllegalArgumentException("The parsed Node is not a Literal");
        }
    }

    @Override
    public Node createLiteral(String content) {
        return Node.createLiteral(content);
    }
    @Override
    public Node createLiteral(String content, Locale language, URI type) {
        return Node.createLiteral(content, 
            language == null ? null : language.getLanguage(), 
            typeMapper.getSafeTypeByName(
                type == null ? null : type.toString()));
    }

    @Override
    public Node createURI(String uri) {
        return Node.createURI(uri);
    }

    @Override
    public String stringValue(Node node) {
        if(node.isLiteral()){
            //we do not want '"example"@en' but 'example'
            return node.getLiteralLexicalForm();
        } else {
            return node.toString();
        }
    }

    @Override
    public Double doubleValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            if(value instanceof Double){
                return (Double)value;
            } else {
                return super.doubleValue(node);
            }
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Long longValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            if(value instanceof Long){
                return (Long)value;
            } else {
                return super.longValue(node);
            }
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Boolean booleanValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            if(value instanceof Boolean){
                return (Boolean)value;
            } else {
                return super.booleanValue(node);
            }
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Date dateTimeValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            try {
                if(value instanceof XSDDateTime){
                    return ((XSDDateTime)value).asCalendar().getTime();
                }
            } catch (RuntimeException e) { /*ignore*/}
            return super.dateTimeValue(node);
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Date dateValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            try {
                if(value instanceof XSDDateTime){
                    return new GregorianCalendar(
                        ((XSDDateTime)value).getYears(), 
                        ((XSDDateTime)value).getMonths() -1, 
                        ((XSDDateTime)value).getDays()).getTime();
                }
            } catch (RuntimeException e) { /*ignore*/}
            return super.dateValue(node);
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Date timeValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            try {
                if(value instanceof XSDDateTime){
                    Calendar cal = ((XSDDateTime)value).asCalendar();
                    cal.set(1900, 0, 1); //we need only the time
                    return cal.getTime();
                }
            } catch (RuntimeException e) { /*ignore*/}
            return super.timeValue(node);
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Float floatValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            if(value instanceof Float){
                return (Float)value;
            } else {
                return super.floatValue(node);
            }
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

    @Override
    public Integer intValue(Node node) {
        if(node.isLiteral()){
            Object value = node.getLiteral().getValue();
            if(value instanceof Integer){
                return (Integer)value;
            } else {
                return super.intValue(node);
            }
        } else {
            throw new IllegalArgumentException("parsed node is not an Literal");
        }
    }

//    @Override
//    public BigInteger integerValue(Node node) {
//        // TODO Auto-generated method stub
//        return null;
//    }

//    @Override
//    public BigDecimal decimalValue(Node node) {
//        // TODO Auto-generated method stub
//        return null;
//    }

    @Override
    public abstract Collection<Node> listObjects(Node subject, Node property);

    @Override
    public abstract Collection<Node> listSubjects(Node property, Node object);

}
