/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.repository;

import fr.gouv.culture.sdx.document.Document;
import fr.gouv.culture.sdx.document.ParsableDocument;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.utils.Identifiable;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;
import fr.gouv.culture.sdx.utils.rdbms.DataSourceComponentBacked;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.cocoon.xml.XMLConsumer;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public abstract class AbstractDataSourceComponentBackedRepository extends DataSourceComponentBacked implements Repository {

    /**True if this is a default repository for an application. */
    protected boolean isDefault;

    /**Constant for a field name in a table*/
    protected final static String FIELD_ID = "id";

    protected final int PARAM_INDEX_FIELD_ID = 1;


    /**Loads base configuration common to all repositories.
     *@param configuration   The configuration object
     *
     * The parameter required are an id for the repository, an optional attribute
     * indicating if the repository is the default within a document base is also handled
     * but the default if not specified will be false.
     *
     * @param   configuration   The configuration for this repository (based on a xml file).
     *
     *<p> Sample configuration entry:
     *<p>&lt;sdx:repository sdx:type = "FS" sdx:id = "myRepoId" baseDirectory = "baseDirName" depth = "2" extent = "50"/>
     *@see #documented_application.xconf we should link to this in the future when we have better documentation capabilities
     *
     *@throws org.apache.avalon.framework.configuration.ConfigurationException
     */
    protected void loadBaseConfiguration(Configuration configuration) throws ConfigurationException {
        super.configure(configuration);
        try {
            super.setId(configuration.getAttribute(Identifiable.ConfigurationNode.ID));
        } catch (SDXException e) {
            String[] args = new String[1];
            args[0] = configuration.getLocation();
            SDXException sdxE = new SDXException(super.getLog(), SDXExceptionCode.ERROR_INVALID_ID_VALUE, args, null);
            throw new ConfigurationException(sdxE.getMessage(), sdxE);
        }
        isDefault = (configuration.getAttributeAsBoolean(Repository.ConfigurationNode.DEFAULT, false));
    }


    public boolean isDefault() {
        return this.isDefault;
    }


    /**Does param checks for subclasses
     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public void delete(Document doc, RepositoryConnection c) throws SDXException {
        Utilities.checkDocument(super.getLog(), doc);
        this.checkConnection(c);
    }

    /**Does param checks for subclasses
     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public void add(Document doc, RepositoryConnection c) throws SDXException {
        Utilities.checkDocument(super.getLog(), doc);
        this.checkConnection(c);
    }

    /**Does param checks for subclasses
     *
     *  @param	doc		    The document to read.
     *	@param	encoding	Should be <code> null</code>  as not verified here, but in subClasses with checkEncoding() method.
     *	@param	c		    A connection to the repository.
     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public InputStream openStream(Document doc, String encoding, RepositoryConnection c) throws SDXException {
        //ensuring we have a valid object
        Utilities.checkDocument(super.getLog(), doc);
        //verifying the encoding, if not valid we use a default, see checkEncoding() for details
        encoding = checkEncoding(encoding);
        this.checkConnection(c);
        return null;
    }

    /**Does param checks for subclasses
     *@throws fr.gouv.culture.sdx.exception.SDXException
     */
    public void get(Document doc, OutputStream os, RepositoryConnection c) throws SDXException {
        Utilities.checkDocument(super.getLog(), doc);
        Utilities.checkOutputStream(super.getLog(), os);
        this.checkConnection(c);
    }

    /**Does param checks for subclasses
     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public void toSAX(ParsableDocument doc, XMLConsumer consumer, RepositoryConnection c) throws SDXException {
        Utilities.checkDocument(super.getLog(), doc);
        Utilities.checkXmlConsumer(super.getLog(), consumer);
        this.checkConnection(c);
    }

    /**Sets the isDefault flag for the repository*/
    public void setIsDefault(boolean b) {
        this.isDefault = b;
    }

    /**Verifies an encoding string,
     * if not supported by JVM default is used, UTF-8.
     *
     * @param encoding
     * @return
     */
    protected String checkEncoding(String encoding) throws SDXException {
        String defaultEncoding = DEFAULT_ENCODING;
        if (Utilities.checkString(encoding)) {
            //verifying the given encoding
            try {
                //TODOException?:will this work, just encoding a string, how to verify the encoding by the JVM see javadocs for System
                defaultEncoding.getBytes(encoding);
                return encoding;
            } catch (UnsupportedEncodingException e) {
                //logging exception
                LoggingUtils.logException(super.getLog(), e);
                //trying the default
                try {
                    defaultEncoding.getBytes(defaultEncoding);
                    //logging info for using default locale
                    //TODORefactor: the string to a class field or TODOException
                    LoggingUtils.logInfo(super.getLog(), "using the default encoding: " + defaultEncoding);
                    return defaultEncoding;
                } catch (UnsupportedEncodingException e1) {
                    String[] args = new String[2];
                    args[0] = defaultEncoding;
                    args[1] = e1.getMessage();
                    throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_DEFAULT_ENCODING, args, null);
                }

            }
        } else {
            //log message that we are using the defaultEncoding
            return defaultEncoding;
        }

    }

    public void checkConnection(RepositoryConnection c) throws SDXException {
        if (c == null) {
            String[] args = new String[1];
            args[0] = getId();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONNECTION_NULL, args, null);
        }
    }

    public void optimize() throws SDXException {
    }

    /** Returns an SQL query that could select a document using its id.
     * <p>
     * This query should have one parameter for the id.
     * @return The query.
     */
    protected String getDocumentGetQuery() {
        return "SELECT * FROM " + getTableName() + " WHERE " + FIELD_ID + " = ?";
    }

    public boolean exists(final String id, RepositoryConnection conn) {
        boolean r_exists = false;
        if (!Utilities.checkString(id)) return r_exists;

        Connection sqlConn = null;
        sqlConn = ((JDBCRepositoryConnection) conn).getConnection();
        String queryString = getDocumentGetQuery();
        Template template = new Template(sqlConn, queryString);
        try {
            QueryExecutor qe = new QueryExecutor() {
                boolean l_exists = false;

                public void prepare(PreparedStatement ps) throws SQLException {
                    ps.setString(PARAM_INDEX_FIELD_ID, id);
                }

                public void collect(ResultSet rs) throws SQLException {
                    if (rs.next()) l_exists = true;
                }

                public boolean exists() {
                    return l_exists;
                }
            };
            template.execute(qe, Template.MODE_EXECUTE_QUERY);
            r_exists = qe.exists();
        } catch (SDXException e) {
            String[] args = new String[3];
            args[0] = id;
            args[1] = getId();
            args[2] = e.getMessage();
            new SDXException(super.getLog(), SDXExceptionCode.ERROR_GET_DOC, args, e);
        }

        return r_exists;

    }

    protected String getClassNameSuffix() {
        return Repository.CLASS_NAME_SUFFIX;
    }

}
