package it.unibz.inf.ontop.rdf4j.repository.impl;

import com.google.inject.Injector;
import it.unibz.inf.ontop.answering.OntopQueryEngine;
import it.unibz.inf.ontop.answering.cache.HTTPCacheHeaders;
import it.unibz.inf.ontop.answering.connection.OntopConnection;
import it.unibz.inf.ontop.query.RDF4JQueryFactory;
import it.unibz.inf.ontop.injection.OntopSystemConfiguration;
import it.unibz.inf.ontop.injection.OntopSystemSettings;
import it.unibz.inf.ontop.rdf4j.repository.OntopRepository;
import it.unibz.inf.ontop.rdf4j.repository.OntopRepositoryConnection;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.repository.RepositoryException;
import org.eclipse.rdf4j.repository.base.AbstractRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.File;

public class OntopVirtualRepository extends AbstractRepository implements OntopRepository {

    private static final Logger logger = LoggerFactory.getLogger(OntopVirtualRepository.class);
    private final OntopSystemSettings settings;

    // Temporary (dropped after initialization)
    @Nullable
    private OntopSystemConfiguration configuration;

    @Nullable
    private OntopQueryEngine queryEngine;
    private final RDF4JQueryFactory inputQueryFactory;
    private final HTTPCacheHeaders cacheHeaders;

    public OntopVirtualRepository(OntopSystemConfiguration configuration) {
        this.configuration = configuration;
        Injector injector = configuration.getInjector();
        inputQueryFactory = injector.getInstance(RDF4JQueryFactory.class);
        cacheHeaders = injector.getInstance(HTTPCacheHeaders.class);
        settings = configuration.getSettings();
    }

    /**
     * Returns a new RepositoryConnection.
     * <p>
     * (No repository connection sharing for the sake
     * of thread-safeness)
     */
    @Override
    public OntopRepositoryConnection getConnection() throws RepositoryException {
        if (!isInitialized()) {
            init();
        }

        try {
            return new OntopRepositoryConnectionImpl(this, getOntopConnection(), inputQueryFactory, settings);
        } catch (Exception e) {
            logger.error("Error creating repo connection: " + e.getMessage());
            throw new RepositoryException(e);
        }
    }


    /**
     * This method leads to the reasoner being initialized (connecting to the database,
     * analyzing mappings, etc.). .
     */
    @Override
    protected void initializeInternal() throws RepositoryException {
        try {
            queryEngine = configuration.loadQueryEngine();
            queryEngine.connect();
            logger.info("Ontop virtual repository initialized successfully!");
        } catch (Exception e) {
            throw new RepositoryException(e);
        }
    }

    /**
     * Returns a connection which can be used to run queries over the repository
     * Before this method can be used, initialize() must be called once.
     */
    private OntopConnection getOntopConnection() throws RepositoryException {
        try {
            return queryEngine.getConnection();
        } catch (Exception e) {
            throw new RepositoryException(e);
        }
    }

    @Override
    public boolean isWritable() throws RepositoryException {
        return false;
    }

    @Override
    protected void shutDownInternal() throws RepositoryException {
        try {
            queryEngine.close();
        } catch (Exception e) {
            throw new RepositoryException(e);
        }
    }

    @Override
    public File getDataDir() {
        throw new RepositoryException("Ontop does not have a data directory");
    }

    @Override
    public ValueFactory getValueFactory() {
        // Gets a ValueFactory for this Repository.
        return SimpleValueFactory.getInstance();
    }

    @Override
    public void setDataDir(File arg0) {
        // Ignores it
    }

    @Override
    public void close() throws RepositoryException {
        this.shutDown();
    }

    @Override
    public HTTPCacheHeaders getHttpCacheHeaders() {
        return cacheHeaders;
    }

    /**
     * Useful for the endpoints: allows to share the same query engine for the SPARQL and the predefined query endpoints
     */
    public OntopQueryEngine getOntopEngine() {
        if (!isInitialized()) {
            init();
        }
        return queryEngine;
    }
}
