/*
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.Utilities;
import fr.gouv.culture.sdx.utils.constants.Node;
import fr.gouv.culture.sdx.utils.database.Database;
import fr.gouv.culture.sdx.utils.database.DatabaseBacked;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;
import fr.gouv.culture.sdx.utils.save.SaveParameters;
import fr.gouv.culture.sdx.utils.save.Saveable;

import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.cocoon.xml.XMLConsumer;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;


/**
 * A basic implementation of a repository. This is a copy of the AbstractRepository
 * class, BUT inherits the functionality of the DatabaseBacked class.
 *
 * <p>
 * This abstract implementation handles the super.getLog(), the id and null operations
 * for connections.
 */
public abstract class AbstractDatabaseBackedRepository extends DatabaseBacked implements Repository {

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

    /**String representation of the "repository" attribute name "read-only".*/
    private final String ATTRIBUTE_READ_ONLY = "read-only";

    /**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 {
        //letting databaseBacked have a chance to configure itself
        super.configure(configuration);

        //we need a valid id
        if (!Utilities.checkString(super.getId())) {
            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));
    }

    /** Releases a previously opened connection.
     *
     * This does nothing in this abstract class.
     *
     *	@param	c	The connection to release.
     */
    public void releaseConnection(RepositoryConnection c) throws SDXException {
        //   if (c != null) c.optimize();
    }

    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] = this.getId();
            throw new SDXException(super.getLog(), SDXExceptionCode.ERROR_CONNECTION_NULL, args, null);
        }
    }

    public void optimize() throws SDXException {
        if(_database!=null) _database.optimize();
    }

    public boolean exists(String id, RepositoryConnection conn) {
        //TODO: what with this connection?
        return _database.entityExists(id);
    }

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

    protected boolean initToSax(){
    	if(!super.initToSax())
    		return false;
    	else{

    		this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));

    		try{
    			String read_only=this._configuration.getAttribute(this.ATTRIBUTE_READ_ONLY);
    		if(read_only != null)
    			this._xmlizable_objects.put("Read_Only",read_only);
    		else
    			this._xmlizable_objects.put("Read_Only","false");
    		} catch(ConfigurationException e){
    			this._xmlizable_objects.put("Read_Only","false");
    		}
    		this._xmlizable_objects.put("Repository_Type",this.getClass().getName());
    		return true;
    	}
    }

    /**Init the LinkedHashMap _xmlizable_volatile_objects with the objects in order to describ them in XML
	 * Some objects need to be refresh each time a toSAX is called*/
	protected void initVolatileObjectsToSax() {
		super.initVolatileObjectsToSax();
		this._xmlizable_objects.put("Is_default",String.valueOf(this.isDefault()));
	}

	/** Save the repository
	 * @see fr.gouv.culture.sdx.utils.save.Saveable#backup(fr.gouv.culture.sdx.utils.save.SaveParameters)
	 */
	public void backup(SaveParameters save_config) throws SDXException {
		if(save_config != null)
		{
			String repo_path = save_config.getUniqueIDString() + "_repo";
			if(save_config.getAttributeAsBoolean(Saveable.ALL_SAVE_ATTRIB,false))
			{
				// add the path to the save_config
				save_config.savePathInConfig(repo_path);

				// Create the documentBases/id_db/repositories/id_repo directory
				File repo_dir = new File(save_config.getStoreCompletePath() + File.separator + repo_path);
				if(!repo_dir.exists())
					repo_dir.mkdir();
				save_config.setAttribute(Node.Name.ID,this.getId());

				SaveParameters data_save = new SaveParameters(Utilities.getElementName(Database.CLASS_NAME_SUFFIX),save_config,repo_path);
				if(_database!=null) this._database.backup(data_save);
			}
		}
	}
	/** Restore the repository
	 * @see fr.gouv.culture.sdx.utils.save.Saveable#restore(fr.gouv.culture.sdx.utils.save.SaveParameters)
	 */
	public void restore(SaveParameters save_config) throws SDXException {

	}

}

