package hirondelle.fish.translate.translation;

import java.util.*;

import hirondelle.fish.translate.basetext.BaseText;
import hirondelle.fish.translate.locale.SupportedLocale;
import hirondelle.web4j.database.DAOException;
import hirondelle.web4j.database.DuplicateException;
import hirondelle.web4j.database.SqlId;
import hirondelle.web4j.database.Db;
import hirondelle.web4j.ui.translate.Translation; 
import hirondelle.web4j.model.Id;
import hirondelle.web4j.config.ConnectionSrc;
import static hirondelle.fish.translate.translation.TranslationEdit.ADD;
import static hirondelle.fish.translate.translation.TranslationEdit.CHANGE;
import static hirondelle.fish.translate.translation.TranslationEdit.DELETE;
import static hirondelle.fish.translate.translation.TranslationEdit.FETCH;
import static hirondelle.fish.translate.translation.TranslationEdit.LIST;

/**
 Data Access Object for translations. 
 
 <P>This DAO is <tt>public</tt> since it is referenced from another package.
 
 <P>Operations performed :
 {@link #FETCH_FROM_CODER_KEYS},
 {@link #FETCH_FROM_NATURAL_LANG_1},
 {@link #FETCH_FROM_NATURAL_LANG_2} in <a href='translation.sql'>SQL Statements</a>. 
*/
public final class TranslationDAO {
  
  public static final SqlId FETCH_FROM_CODER_KEYS = getSqlId("FETCH_TRANSLATIONS_FROM_CODER_KEYS");
  /*
   Important Note : 
   This separation into ONE + TWO exists only because the target db used in dev has no UNION operation.
   If the target database used in dev was more mature, then a much more compact implementation would be possible. 
  */
  public static final SqlId FETCH_FROM_NATURAL_LANG_1 = getSqlId("FETCH_TRANSLATIONS_FROM_NATURAL_LANGUAGE_KEYS_ONE");
  public static final SqlId FETCH_FROM_NATURAL_LANG_2 = getSqlId("FETCH_TRANSLATIONS_FROM_NATURAL_LANGUAGE_KEYS_TWO");
  
  /**
   Return Map[BaseText, Map[Locale, Translation]].
   
   <P>Calls {@link Translation#asNestedMap(java.util.Collection)}.
  */
  public Map<String, Map<String, String>> getTranslations() throws DAOException {
    return Translation.asNestedMap(list());
  }

  /**
   Return a {@link List} of all {@link Translation} objects.
   
   <P>Order by base text, then by {@link Locale}.
  */
  List<Translation> list() throws DAOException {
    List<Translation> transForCoderKeys = Db.list(Translation.class, FETCH_FROM_CODER_KEYS);
    List<Translation> transForLangOne = Db.list(Translation.class, FETCH_FROM_NATURAL_LANG_1);
    List<Translation> transForLangTwo = Db.list(Translation.class, FETCH_FROM_NATURAL_LANG_2);
    
    //remove possible dupes 
    Set<Translation> noDupes = new LinkedHashSet<Translation>();
    noDupes.addAll(transForCoderKeys);
    noDupes.addAll(transForLangOne);
    noDupes.addAll(transForLangTwo);
    List<Translation> result = new ArrayList<Translation>(noDupes);
    Collections.sort(result);
    return result;
  }
  
  /**
   Return a {@code List<Translation>}, containing all translations for the given <tt>BaseTextId</tt>.
   <P>A translation for the base language (English) is returned only if the {@link BaseText} is a coder key.
  */
  List<Translation> listFor(Id aBaseTextId) throws DAOException {
    return Db.list(Translation.class, LIST, aBaseTextId);
  }
  
  /**
   Add a new {@link Translation}.
   
   <P>For a given {@link BaseText}, there can be only one translation for a given {@link SupportedLocale}.
  */
  void add(Translation aTranslation) throws DAOException, DuplicateException {
    Object[] params = {aTranslation.getBaseTextId(), aTranslation.getLocaleId(), aTranslation.getTranslation()};
    Db.edit(ADD, params);
  }
  
  /**
   Return a single {@link Translation}.
   
   <P>No {@link Translation} is returned if {@link BaseText} has a natural language key, and 
   the <tt>Locale</tt> is the base locale.
  */
  Translation fetch(Id aBaseTextId, Id aLocaleId) throws DAOException {
    return Db.fetch(Translation.class, FETCH, aBaseTextId, aLocaleId);
  }
  
  /**
   Update the text of a {@link Translation}.
   
   <P>The <tt>Locale</tt> of the {@link Translation} is not altered by this method. 
  */
  void change(Translation aTranslation) throws DAOException {
    Object[] params = {aTranslation.getTranslation(), aTranslation.getBaseTextId(), aTranslation.getLocaleId()};
    Db.edit(CHANGE, params);
  }
  
  /** Delete an exiting {@link Translation}. */
  void delete(Id aBaseTextId, Id aLocaleId) throws DAOException {
    Db.edit(DELETE, aBaseTextId, aLocaleId);
  }
  
  // PRIVATE //
  private static SqlId getSqlId(String aSqlId){
    return new SqlId(ConnectionSrc.TRANSLATION, aSqlId);
  }
}