/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2005 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.data;

import java.io.File;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;

import org.w3c.dom.Document;

import fr.gouv.finances.cp.utils.Pair;
import fr.gouv.finances.cp.xemelios.common.PJRef;
import fr.gouv.finances.cp.xemelios.common.config.ChampModel;
import fr.gouv.finances.cp.xemelios.common.config.CritereModel;
import fr.gouv.finances.cp.xemelios.common.config.DocumentModel;
import fr.gouv.finances.cp.xemelios.common.config.DocumentsModel;
import fr.gouv.finances.cp.xemelios.common.config.ElementModel;
import fr.gouv.finances.cp.xemelios.common.config.EtatModel;
import fr.gouv.finances.cp.xemelios.common.config.ListeResultatModel;
import fr.gouv.finances.cp.xemelios.common.config.RecherchePaireModel;
import fr.gouv.finances.cp.xemelios.data.impl.sqlconfig.TPersistenceConfig;
import fr.gouv.finances.cp.xemelios.export.ConfigModel;
import fr.gouv.finances.cp.xemelios.ui.DatabaseTreeDataDonnee;
import fr.gouv.finances.cp.xemelios.ui.DatabaseTreeDataFichier;

/**
 * Defines what a data implementation must implement
 * @author chm
 */
public interface DataImpl {
    /**
     * if element to import exists, do nothing
     */
    public static final int OVERWRITE_NEVER = 1;
    /**
     * if element to import exists, but is older, overwrite it
     */
    public static final int OVERWRITE_IF_NEWER = 2;
    /**
     * if element to import exists, always delete it before to write the newer one.
     */
    public static final int OVERWRITE_ALWAYS = 3;
    /**
     * if element to import exists, write a new one beside the existing
     */
    public static final int OVERWRITE_DUPLICATE = 4;
    /**
     * Read/Write access
     */
    public static final int READ_WRITE_PASS=0;
    /**
     * Read Only access
     */
    public static final int READ_ONLY_PASS=1;
    
    /**
     * Les diff�rents niveaux de donn�es exportable.
     */
    public static final int EXPORT_SK1 = DatabaseTreeDataDonnee.DataNode.TYPE_PERSO1;
    public static final int EXPORT_SK2 = DatabaseTreeDataDonnee.DataNode.TYPE_PERSO2;
    public static final int EXPORT_FIC = DatabaseTreeDataFichier.Data.LVL_FIC;
    public static final int EXPORT_BUD = DatabaseTreeDataFichier.Data.LVL_BUD;
    public static final int EXPORT_COL = DatabaseTreeDataFichier.Data.LVL_COL;
    public static final int EXPORT_DOC = DatabaseTreeDataFichier.Data.LVL_DOC;
    
    
    
    // TODO: fill this
    /**
     * Returns the registred name of this persistence layer
     */
    public String getLayerName();
    /**
     * Defines the directory where config files are.
     * This method is called by {@link fr.gouv.finances.xemelios.data.DataLayerManager}
     * when this data implementation is loaded.
     */
    public void setDocumentsConfigDirectory(File directory);
    
    /**
     * Retrieves the persistence configuration for this EtatModel
     * // TODO: change name Etat
     * @param em
     * @return
     */
    public TPersistenceConfig getPersistenceConfig(DocumentModel dm)  throws DataConfigurationException ;
    
    /**
     * Declares (if not already done) that data has been imported for this (DocumentModel,budget,collectivite)
     * Actual implementations may cache registered data.
     * @param dm Document type
     * @param codeBudget budget
     * @param collectivite collectivite
     */
    public void registerBudgetCollectivite(DocumentModel dm,Pair codeBudget,Pair collectivite, String docName) throws DataConfigurationException, DataAccessException;
    
    /**
     * Import a element. This method <strong>MUST</strong> handle correctly warnings.
     * @param dm Document type
     * @param currentEtat Etat
     * @param codeBudget The budget this element belongs to
     * @param collectivite The collectivite this element belongs to
     * @param sourceFileName name of source file
     * @param elementName generated element name
     * @param element the element to import
     * @param overwriteMode Comportement to use in case of overwrite. See OVERWRITE_* constants.
     * @return <tt>true</tt> if data has been written without warning, <tt>false</tt> if warnings occured.
     * @throws DataAccessException if data has <strong>not</strong> been written. See message for details
     */
    public boolean importElement(DocumentModel dm,EtatModel currentEtat, Pair codeBudget, Pair collectivite, String sourceFileName, String elementName, String strElement, String encoding) throws DataConfigurationException, DataAccessException;
    
    /**
     * Save repository. Overwrite existing one if necessary.
     * @param dm Document type
     * @param collectivite The collectivite this repostory belongs to
     * @return Should always return <tt>true</tt> or throw an exception
     * @throws DataAccessException
     */
    public boolean saveRepository(DocumentModel dm, Pair collectivite, Document repository) throws DataConfigurationException, DataAccessException;
    
    /**
     * Removes a document from persistence system
     * @param dm Document type
     * @param budget
     * @param collectivite
     * @param docName The name fo document to remove
     * @throws DataConfigurationException If an error occurs while removing document
     * @throws DataAccessException If a configuration error occurs
     */
    public void removeDocument(DocumentModel dm, Pair budget, Pair collectivite, String docName) throws DataConfigurationException, DataAccessException;
    
    /**
     * Checks if the designated document has already been imported
     * @param dm Document type
     * @param budget
     * @param collectivite
     * @param docName The name of document to remove
     * @return <tt>true</tt> if document exists, <tt>false</tt> otherwise
     * @throws DataConfigurationException If an error occurs while removing document
     * @throws DataAccessException If a configuration error occurs
     */
    public boolean isDocumentExists(DocumentModel dm, Pair budget, Pair collectivite, String docName) throws DataConfigurationException, DataAccessException;
    
    /**
     * Save these special keys in underlying data layer.
     * If <tt>sk1</tt>, <tt>sk2</tt> and <tt>sk3</tt> are null, does nothing.
     * @param dm Document type
     * @param budget
     * @param collectivite
     * @param docName
     * @param sk1 Special key 1
     * @param sk2 Special key 2
     * @param sk3 Special key 3
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public void saveSpecialKeys(DocumentModel dm,Pair budget,Pair collectivite,String docName,Pair sk1, Pair sk2, Pair sk3) throws DataConfigurationException, DataAccessException;
    /**
     * Returns <tt>true</tt> if database contains documents on this DocumentModel and on this EtatModel
     * @param dm
     * @param em
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean canSearch(DocumentModel dm, EtatModel em) throws DataConfigurationException, DataAccessException;
    /**
     * Return the registered special keys of first level for this (<tt>dm</tt>, <tt>collectivite</tt>, <tt>budget</tt>)
     * @param dm
     * @param collectivite
     * @param budget
     * @param distinct
     * @return A <tt>Vector&lt;Pair&gt;</tt> of special keys
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Vector<Pair> getSpecialKeys1(DocumentModel dm,Pair collectivite,Pair budget,boolean distinct) throws DataConfigurationException, DataAccessException;
    /**
     * Return the registered special keys of second level for this (<tt>dm</tt>, <tt>collectivite</tt>, <tt>budget</tt>, <tt>key1</tt>)
     * @param dm
     * @param collectivite
     * @param budget
     * @param key1
     * @param distinct
     * @return A <tt>Vector&lt;Pair&gt;</tt> of special keys
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Vector<Pair> getSpecialKeys2(DocumentModel dm,Pair collectivite,Pair budget,Pair key1,boolean distinct) throws DataConfigurationException, DataAccessException;
    /**
     * Return the registered special keys of second level for this (<tt>dm</tt>, <tt>collectivite</tt>, <tt>budget</tt>, <tt>key1</tt>, <tt>key2</tt>)
     * @param dm
     * @param collectivite
     * @param budget
     * @param key1
     * @param key2
     * @param distinct
     * @return A <tt>Vector&lt;Pair&gt;</tt> of special keys
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Vector<Pair> getSpecialKeys3(DocumentModel dm,Pair collectivite,Pair budget,Pair key1,Pair key2,boolean distinct) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the list of collectivites that have documents of type <tt>dm</tt>
     * @param dm
     * @return A <tt>Vector&lt;Pair&gt;</tt>
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Vector<Pair> getCollectivites(DocumentModel dm) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the list of budgets that have documents of type <tt>dm</tt> for this <tt>collectivite</tt>
     * @param dm
     * @param collectivite
     * @return <tt>Vector&lt;Pair&gt;</tt>
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Vector<Pair> getBudgets(DocumentModel dm,Pair collectivite) throws DataConfigurationException, DataAccessException;
    /**
     * Queries the repository of <tt>dm</tt> with the specified query <tt>rpm</tt> for the specified <tt>collectivite</tt>
     * @param dm The document-repository to query
     * @param collectivite the collectivite to query the repository on.
     * @param rpm The query
     * @return A {@link Collection} of {@link Pair} that contains the matching repository entries.
     * @throws DataConfigurationException If a invlid configuration is provided
     * @throws DataAccessException If a problem ooccurs while accessing the repository.
     */
    public Collection<Pair> queryRepository(DocumentModel dm,Pair collectivite,RecherchePaireModel rpm) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the repository for the document type and the collectivite.
     * @param dm
     * @param collectivite
     * @param budget Always ignored as repoository are per-(DocumentModel/Collectivite)
     * @return The Xml
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public String getRepository(DocumentModel dm, Pair collectivite, Pair budget) throws DataConfigurationException, DataAccessException;
    /**
     * Return the repository as a dom object.
     * @param dm
     * @param collectivite
     * @param budget
     * @return dom object
     */
    public Document getRepositoryAsDom(DocumentModel dm, Pair collectivite) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the distinct values for this <tt>path</tt>
     * @param em
     * @param collectivite
     * @param budget
     * @param path
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Collection<String> getDistinctValues(ElementModel em, Pair collectivite, Pair budget, String path) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to the specified DocumentModel
     * @param dm
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeDocumentModel(DocumentModel dm) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to the specified DocumentModel AND collectivite
     * @param dm
     * @param collectivite
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeCollectivite(DocumentModel dm, Pair collectivite) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to specified DocumentModel, collectivite and  budget
     * @param dm
     * @param collectivite
     * @param budget
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeBudget(DocumentModel dm, Pair collectivite, Pair budget) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data correcponding to specified DocumentModel and fichier
     * @param dm
     * @param fichier
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeFichier(DocumentModel dm, String fichier) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to specified DocumentModel, collectivite, budget and special key 1
     * @param dm
     * @param collectivite
     * @param budget
     * @param key1
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeSpecialKey1(DocumentModel dm, Pair collectivite, Pair budget, Pair key1) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to specified DocumentModel, collectivite, budget, special key 1 and 2
     * @param dm
     * @param collectivite
     * @param budget
     * @param key1
     * @param key2
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeSpecialKey2(DocumentModel dm, Pair collectivite, Pair budget, Pair key1, Pair key2) throws DataConfigurationException, DataAccessException;
    /**
     * Removes all data corresponding to specified DocumentModel, collectivite, budget, special key 1, 2 and 3
     * @param dm
     * @param collectivite
     * @param budget
     * @param key1
     * @param key2
     * @param key3
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean removeSpecialKey3(DocumentModel dm, Pair collectivite, Pair budget, Pair key1, Pair key2, Pair key3) throws DataConfigurationException, DataAccessException;
    /**
     * Returns an implementation of an empty result set. Very usefull when error occurs....
     * @return What you asked for.
     */
    public DataResultSet getEmptyDataResultSet();
    /**
     * Search the database for specified criterias.
     * This method is called by the UI when the user wants to query a list of documents and to display
     * the results in the result table.
     * @param elementModel	The elementModel to retrieve
     * @param collectivite	The collectivite where to search in
     * @param budget		The budget where to search in
     * @param xPath			The XPath to apply
     * @param lrm			The ListeResultatModel to apply to display in the table
     * @param models		The criterias typed by the user (this is to help the underlying implementation to translate from XPath to implementation query language)
     * @return				A DataResultSet that can be given to the result table.
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public DataResultSet search(ElementModel elementModel, Pair collectivite, Pair budget, String xPath,ListeResultatModel lrm,Vector<CritereModel> models) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the list of available operators for the underlying implementation for the given datatype
     * @param datatype
     * @return
     */
    public Pair[] getOperators(String datatype);
    /**
     * Returns the specified document as a string.
     * @param etatModel
     * @param collectivite
     * @param budget
     * @param docId
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public String getDocumentAsString(EtatModel etatModel, Pair collectivite, Pair budget, String docId) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the specified document as a DOM Document
     * @param etatModel
     * @param collectivite
     * @param budget
     * @param docId
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public Document getDocumentAsDom(EtatModel etatModel, Pair collectivite, Pair budget, String docId) throws DataConfigurationException, DataAccessException;
    /**
     * Returns the specified document infos as a DocumentInfos
     * @param etatModel
     * @param collectivite
     * @param budget
     * @param docId
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public DocumentInfos getDocumentInfos(EtatModel etatModel, Pair collectivite, Pair budget, String docId) throws DataConfigurationException, DataAccessException;
    /**
     * This method imports a PJRef object in data layer.
     * @param pj
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public void importPj(PJRef pj) throws DataConfigurationException, DataAccessException;
    /**
     * This method retreives the content of a PJ as a byte array.
     * @param collectivite
     * @param pjName PJ name
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public PJRef getPj(Pair collectivite, String pjName) throws DataConfigurationException, DataAccessException;
    /**
     * This method retreives the content of a PJ as a byte array.
     * @param collectivite
     * @param id
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public PJRef getPjByUniqueId(Pair collectivite, String id) throws DataConfigurationException, DataAccessException;
    /**
     * This method retreives the content of a PJ as a byte array.
     * @param collectivite
     * @param id
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public boolean isPjAvailable(Pair collectivite, String id) throws DataConfigurationException, DataAccessException;
    /**
     * This method reset all cached data and configuration. Next call to any other
     * method will reload configuration files. This does not include database access configuration.
     * Implementors should throw an {@link java.lang.IllegalStateException} if they
     * do not implement this method.
     */
    public void reset();
    /**
     * This method returns the last warnings that occured in the <strong>current</strong> thread.
     * @return The last warnings
     */
    public String getLastWarnings();
    
    /**
     * This method retrieves the document's id that match criterias. It is usually called by UI to retrieve the root-browsable document.
     * @param em
     * @param colletivite
     * @param budget
     * @param sp1 The special key 1. Pass <tt>null</tt> to ignore.
     * @param sp2 The special key 2. Pass <tt>null</tt> to ignore.
     * @param sp3 The special key 3. Pass <tt>null</tt> to ignore.
     * @return
     * @throws DataAccessException
     * @throws DataConfigurationException
     */
    public ArrayList<String> getDocumentListFromSpecialKeys(ElementModel em, String colletivite, String budget, String sp1, String sp2, String sp3) throws DataConfigurationException, DataAccessException ;
    /**
     * This method does the same job than {@link getDocumentListFromSpecialKeys} but it tries to apply an xpath filter on result.
     * Implementations may limit the xpath syntax to use in this method and should so throw an {@link DataConfigurationException}
     * with an explicit message, such as "XPath too complicated"
     * @param em
     * @param collectivite
     * @param budget
     * @param sp1
     * @param sp2
     * @param sp3
     * @param path
     * @return
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public ArrayList<String> getDocumentListFromXPath(ElementModel em, String collectivite, String budget, String sp1, String sp2, String sp3, String path) throws DataConfigurationException, DataAccessException ;
    public SimpleDateFormat getDateFromatter();
    /**
     * This method returns a list of operators that can be used on the given data type, for instance sum, average, max, min, etc...
     * @param datatype The type of data we want to know the operators for
     * @return An array of Pair objects representing the operators, or an empty Array if no operator is applicable.
     */
    public Pair[] getAggregateOperators(String datatype);
    
    /**
     * TODO
     * @param champId
     * @param operator
     * @param cm Informations on how data is displayed in column
     * @param rs The {@link DataResultSet} used for the original request
     * @return The formatted result
     * @throws DataConfigurationException
     * @throws DataAccessException
     */
    public String calculateAggregate(DocumentModel dm, EtatModel em, String columnXPath, String operator, ChampModel cm, DataResultSet rs)
    throws DataConfigurationException, DataAccessException;
    
    /**
     * Ins�re une nouvelle configuration d'export.
     * @param cm la repr�sentation de la configuration
     */
    public void insertConfigExport(ConfigModel cm);
    
    /**
     * Met � jour une configuration d'export.
     * @param cm la repr�sentation de la configuration
     */
    public void updateConfigExport(ConfigModel cm);
    
    /**
     * Recherche la liste des configurations d'export pour un etat d'un document.
     * @param docID
     * @param etatID
     * @return
     */
    public Vector<ConfigModel> getListeConfigsExport(String docID, String etatID);
    
    /**
     * Supprime la configuration d'export identifi�e par cm
     * @param cm
     */
    public void deleteConfigExport(ConfigModel cm);
    
    
    /**
     * Recherche les fichiers import�s correspondant aux crit�res pass�s.
     * seul le DocumentModel est obligatoire.
     * Le r�sultat est stock� dans un objet servant au Panel de la recherche de fichiers.
     * @param dm
     * @param coll
     * @param budg
     * @return
     */
    public DatabaseTreeDataFichier.Data searchFichiers(DocumentModel dm, String coll, String budg) throws DataAccessException, DataConfigurationException;
    
    /**
     * Recherche les fichiers import�s correspondant aux crit�res pass�s.
     * Aucun crit�re n'est obligatoire.
     * Le r�sultat est stock� dans un objet servant au Panel de la recherche de fichiers.
     * @param dms
     * @param coll
     * @param budg
     * @return
     * @throws DataAccessException
     * @throws DataConfigurationException
     */
    public Vector<DatabaseTreeDataFichier.Data> searchFichiers(DocumentsModel dms, String coll, String budg) throws DataAccessException, DataConfigurationException;
    
    /**
     * Recherche dans la base les �l�ments � exporter en fonction du type de donn�e (whatToExport) et des param�tres identifiants
     * @param whatToExport Le niveau des donn�es � exporter
     * @param em L'ElementModel
     * @param coll La collectivit�
     * @param budg Le budget
     * @param fic Le fichier improt�
     * @param SK1 La special Key 1
     * @param SK2 La special Key 2
     * @return
     * @throws DataConfigurationException
     * @throws SQLException
     */
    public DataResultSet getDataResultSetForExport(int whatToExport, ElementModel em, Pair coll, Pair budg, String fic, Pair SK1, Pair SK2) throws DataConfigurationException, SQLException;
}
