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

import fr.gouv.culture.sdx.documentbase.DefaultIDGenerator;
import fr.gouv.culture.sdx.documentbase.DocumentBase;
import fr.gouv.culture.sdx.documentbase.IDGenerator;
import fr.gouv.culture.sdx.documentbase.LuceneDocumentBase;
import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.framework.FrameworkImpl;
import fr.gouv.culture.sdx.repository.Repository;
import fr.gouv.culture.sdx.search.lucene.FieldList;
import fr.gouv.culture.sdx.utils.SdxObject;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.constants.ContextKeys;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.logger.Logger;

public class ConfigurationUtils {

    public static IDGenerator configureIDGenerator(Logger logger, Configuration configuration) throws ConfigurationException {
        IDGenerator idGen = new DefaultIDGenerator();//we use the sdx default
        Configuration idGenConf = configuration.getChild(IDGenerator.ConfigurationNode.ID_GENERATOR, false);
        if (idGenConf != null) {
            try {
                //we attempt to use what is given
                String idGenClassName = idGenConf.getAttribute(IDGenerator.ConfigurationNode.CLASS, IDGenerator.ConfigurationNode.Value.DEFAULT_ID_GENERATOR_CLASS);
                Object l_obj = Utilities.getObjectForClassName(logger, idGenClassName, IDGenerator.PACKAGE_QUALNAME, idGenClassName, IDGenerator.CLASS_NAME_SUFFIX);
                Class l_objClass = l_obj.getClass();

                if (!(l_obj instanceof IDGenerator)) {
                    //the object doesn't implement our interface
                    String[] args = new String[3];
                    args[0] = IDGenerator.CLASS_NAME_SUFFIX;
                    if (l_objClass != null)
                        args[1] = l_objClass.getName();
                    args[2] = idGenClassName;
                    throw new SDXException(logger, SDXExceptionCode.ERROR_CLASS_NOT_INSTANCEOF_SDX_INTERFACE, args, null);
                }

                idGen = (IDGenerator) l_obj;
                idGen.configure(idGenConf);
            } catch (SDXException e) {
                throw new ConfigurationException(e.getMessage(), e);
            }
        }

        return idGen;
    }

    public static FieldList configureFieldList(Logger logger, ServiceManager manager, org.apache.avalon.framework.context.Context context, String id, Configuration configuration) throws ConfigurationException {
        //Lucene specific configuration
        FieldList fieldList = null;
        //at this point, we should have <sdx:fieldList> containing a list of fields
        try {
            //getting the list of fields for indexing

            Configuration fieldListConf = null;

            if (configuration.getName().equals(DocumentBase.ConfigurationNode.FIELD_LIST))
                fieldListConf = configuration;
            else
                fieldListConf = configuration.getChild(DocumentBase.ConfigurationNode.FIELD_LIST, false);

            //testing if we have something
            if (fieldListConf == null) {
                String[] args = new String[1];
                args[0] = configuration.getLocation();
                SDXException sdxE = new SDXException(logger, SDXExceptionCode.ERROR_NO_FIELD_LIST_CONFIG, args, null);
                throw new ConfigurationException(sdxE.getMessage(), sdxE);
            }

            //assigning a value for the class field
            fieldList = new FieldList();
            //setting the super.getLog()
            Utilities.setUpSdxObject(fieldList, logger, context, manager);
            if (Utilities.checkString(id))//if not null we set it and the configuration can override
                fieldList.setId(id);
            //configuring
            fieldList.configure(fieldListConf);

            return fieldList;
        } catch (SDXException e) {
            throw new ConfigurationException(e.getMessage(), e);
        }
    }

    public static FieldList configureLuceneFieldList(Logger logger, Context context, FieldList fieldList) throws ConfigurationException {
        if (context != null) {
            //we add SDX internal fields
            try {
                Configuration sdxConf = (Configuration) context.get(ContextKeys.SDX.Framework.CONFIGURATION_FILE);
                //testing if we have something
                if (sdxConf == null) {
                    String[] args = new String[2];
                    args[0] = FrameworkImpl.CONFIGURATION_FILE_NAME;
                    args[1] = Utilities.getStringFromContext(ContextKeys.SDX.Framework.CONFIGURATION_PATH, context);
                    SDXException sdxE = new SDXException(logger, SDXExceptionCode.ERROR_NO_SDX_CONFIG_FILE, args, null);
                    throw new ConfigurationException(sdxE.getMessage(), sdxE);
                }
                Configuration iFConf = sdxConf.getChild(LuceneDocumentBase.ELEMENT_NAME_LUCENE_SDX_INTERNAL_FIELDS, false);
                //testing if we have something
                if (iFConf == null) {
                    //TODOException?:should we be warning here instead of throwing an exception?-rbp
                    String[] args = new String[1];
                    args[0] = sdxConf.getLocation();
                    SDXException sdxE = new SDXException(logger, SDXExceptionCode.ERROR_NO_INTERNAL_FIELDS_CONFIG, args, null);
                    throw new ConfigurationException(sdxE.getMessage(), sdxE);
                }
                //configuring internal field for term searching
                fieldList.addInternalFields(iFConf);
            } catch (ContextException e) {
                throw new ConfigurationException(e.getMessage(), e);
            } catch (SDXException e) {
                throw new ConfigurationException(e.getMessage(), e);
            }
        }

        return fieldList;
    }

    /**Used to create a repository based upon a 'type' attribute
     *
     * @param repoConf  The configuration object made up of an 'repository' element.
     * @param manager   The service manager to pass to the repository
     * @param context     The context object to pass to the repository
     * @param logger    The super.getLog() to pass to the repository and for error handling
     * @throws fr.gouv.culture.sdx.exception.SDXException
     */
    public static Repository createRepository(Logger logger, org.apache.avalon.framework.context.Context context, ServiceManager manager, Configuration repoConf) throws SDXException, ConfigurationException {
        //TODOException?:throw exceptions below super.getLog() may not be necessary-figure this out
        //at this point we have <sdx:repository> element
        Repository repo = null;
        //reading the 'type' attribute
        String repoType = repoConf.getAttribute(SdxObject.ConfigurationNode.TYPE);
        //verifying the attribute
        ConfigurationUtils.checkConfAttributeValue(SdxObject.ConfigurationNode.TYPE, repoType, repoConf.getLocation());
        Object l_obj = Utilities.getObjectForClassName(logger, repoType, Repository.PACKAGE_QUALNAME, repoType, Repository.CLASS_NAME_SUFFIX);
        Class l_objClass = l_obj.getClass();

        if (!(l_obj instanceof Repository)) {
            //the object doesn't implement our interface
            String[] args = new String[3];
            args[0] = Repository.CLASS_NAME_SUFFIX;
            if (l_objClass != null)
                args[1] = l_objClass.getName();
            args[2] = repoType;
            throw new SDXException(logger, SDXExceptionCode.ERROR_CLASS_NOT_INSTANCEOF_SDX_INTERFACE, args, null);
        }
        //casting into a repository object
        repo = (Repository) l_obj;
        repo = (Repository) Utilities.setUpSdxObject(repo, logger, Utilities.createNewReadOnlyContext(context), manager);
        //configuring the repo
        repo.configure(repoConf);
        //initializing the repo
        repo.init();
        return repo;
    }

    /**Verifies a configuration attribute value, ie it should not be null or an empty String
     *
     * @param attName       The name of the attribute
     * @param attValue      The value of the attribute
     * @param attLocation   The location of the configuration object
     * @throws org.apache.avalon.framework.configuration.ConfigurationException   Thrown if <code>null</code> or an empty String
     */
    public static void checkConfAttributeValue(String attName, String attValue, String attLocation) throws ConfigurationException {
        /*a null check is done for safety, but a null string value should not be passed
        *to this method as the message given will not be accurate, but will still be helpful*/
        if (!Utilities.checkString(attValue)) {
            String[] args = new String[2];
            args[0] = attName;
            args[1] = attLocation;
            //null super.getLog() to avoid double logging
            SDXException sdxE = new SDXException(SDXExceptionCode.ERROR_INVALID_ATTRIBUTE, args);
            throw new ConfigurationException(sdxE.getMessage(), sdxE);
        }
    }


    /**Verifies a configuration object
     *
     * @param configuration The object to verify
     * @throws org.apache.avalon.framework.configuration.ConfigurationException   Thrown if the object is null
     */
    public static void checkConfiguration(Configuration configuration) throws ConfigurationException {
        if (configuration == null) {
            SDXException sdxE = new SDXException(null, SDXExceptionCode.ERROR_CONFIG_NULL, null, null);
            throw new ConfigurationException(sdxE.getMessage(), sdxE);
        }
    }

    public static void checkLogger(Logger log) throws ConfigurationException {
        //TODOException: create an sdx exception and wrap it like in checkConfiguration
        if (log == null) throw new ConfigurationException("A Logger object was null or was not provided");

    }

    public static void checkServiceManager(ServiceManager serviceManager) throws ServiceException {
        //TODOException: create an sdx exception and wrap it like in checkConfiguration
        if (serviceManager == null){
            SDXException sdxE =  new SDXException(null, SDXExceptionCode.ERROR_SERVICE_MANAGER_NULL, null, null);
            throw new ServiceException("", sdxE.getMessage(), sdxE);
        }
    }

    public static void checkContext(org.apache.avalon.framework.context.Context context) throws ContextException {
        //TODOException: create an sdx exception and wrap it like in checkConfiguration
        if (context == null) throw new ContextException("A ContextKeys object was null or was not provided");
    }


}
