#include "../../MedinTuxTools-QT4/CGestIni.h"
#include "C_PosologieGrammar.h"

//=================== C_PosologieGrammar ===================================
/*! \class C_PosologieGrammar
 *  \brief Class to set and interpret posologie grammar.
 */
//---------------------------- C_PosologieGrammar ----------------------------------------------
C_PosologieGrammar::C_PosologieGrammar(const QString &grammar_expression)
{setGrammar(grammar_expression);
}
//---------------------------- setGrammar ----------------------------------------------
/*! \brief set the literal grammar string. if empty a empty skeleton grammar  is set
 *  the grammar string is updated whith flags contents indications
 *  \param const QString &grammar_expression :   grammar string to set
 *  \return the grammar string.
*/
QString C_PosologieGrammar::setGrammar(const QString &grammar_expression)
{m_grammar_expression = grammar_expression;
 if (m_grammar_expression.length()==0)
    {m_grammar_expression = empty_skeleton_grammar();
    }

 return m_grammar_expression;
}
//---------------------------- setLiteralFlag ----------------------------------------------
/*! \brief set how the grammar must be converted
 *  \param int must_be_literal (default = C_PosologieGrammar::ALL_LITERAL).
 *   if  must_be_literal is set to  undefined if -1 (the grammar string try to be used for this and if not possible it's defined to C_PosologieGrammar::NOT_LITERAL)
 *         otherwise it's must be a combination of : C_PosologieGrammar::UP_QUANTITE
 *                                                   C_PosologieGrammar::UP_JUST_FRACTION   //  conversion uniquement de 0.25 en un quart 0.5 en un demi 0.75 en trois quart
 *                                                   C_PosologieGrammar::DURATION
 *                                                   C_PosologieGrammar::SEQUENCE_HOUR
 *                                                   C_PosologieGrammar::MOMENT_MEAL
 *                                                   C_PosologieGrammar::INDICATION_LIST   indications list must be showed
 *                                                   C_PosologieGrammar::INDICATION_CODE   indications code must be showed
 *                                                   C_PosologieGrammar::INDICATION_TYPE   indications code typ (CIP CISP etc.) must be showed
 *         this parameter define how numerics values must be converted in literal string.
 */
void C_PosologieGrammar::setLiteralFlag(int must_be_literal    /* = C_PosologieGrammar::ALL_LITERAL */)
{m_must_be_literal = must_be_literal;
 CGestIni::setXmlData("gph_cm", literalFlagToString(must_be_literal).toAscii(),  m_grammar_expression);
}
//---------------------------- isLiteral ----------------------------------------------
/*! \brief return the literal int flag. if zero nothing must be in literal form
 *  \return the literal int flag.
*/
int C_PosologieGrammar::isLiteral()
{return m_must_be_literal;
}
//---------------------------- literalFlagToString [static] ------------------------------------------------
/*! \brief return a literal string from int flag.  [static]
 *  \param int must_be_literal :    int flag
 *  \return literal string.
*/
QString C_PosologieGrammar::literalFlagToString(int must_be_literal)
{QString ret = "";
if (must_be_literal & C_PosologieGrammar::UP_QUANTITE)        ret += "UP_QUANTITE ";
if (must_be_literal & C_PosologieGrammar::DURATION)           ret += "DURATION ";
if (must_be_literal & C_PosologieGrammar::SEQUENCE_HOUR)      ret += "SEQUENCE_HOUR ";
if (must_be_literal & C_PosologieGrammar::MOMENT_MEAL)        ret += "MOMENT_MEAL ";
if (must_be_literal & C_PosologieGrammar::UP_JUST_FRACTION)   ret += "UP_JUST_FRACTION ";
return ret;
}
//---------------------------- literalStringToFlag [static] ------------------------------------------------
/*! \brief return the literal flag from literal string.  [static]
 *  \param const QString &conv_mod :  literal string to convert in flag
 *  \return literal flag.
*/
int C_PosologieGrammar::literalStringToFlag(const QString &conv_mod)
{int must_be_literal = 0;
 if (conv_mod.contains("UP_QUANTITE"))        must_be_literal |= C_PosologieGrammar::UP_QUANTITE;
 if (conv_mod.contains("DURATION"))           must_be_literal |= C_PosologieGrammar::DURATION;
 if (conv_mod.contains("SEQUENCE_HOUR"))      must_be_literal |= C_PosologieGrammar::SEQUENCE_HOUR;
 if (conv_mod.contains("MOMENT_MEAL"))        must_be_literal |= C_PosologieGrammar::MOMENT_MEAL;
 if (conv_mod.contains("ALL_LITERAL"))        must_be_literal  = C_PosologieGrammar::ALL_LITERAL;
 if (conv_mod.contains("NOT_LITERAL"))        must_be_literal  = C_PosologieGrammar::UP_JUST_FRACTION;
 if (conv_mod.contains("INDICATION_LIST"))    must_be_literal |= C_PosologieGrammar::INDICATION_LIST;
 if (conv_mod.contains("INDICATION_CODE"))    must_be_literal |= C_PosologieGrammar::INDICATION_CODE;
 if (conv_mod.contains("INDICATION_TYPE"))    must_be_literal |= C_PosologieGrammar::INDICATION_TYPE;
 return must_be_literal;
}
//---------------------------- empty_skeleton_grammar [static] ------------------------------------------------
/*! \brief return a empty grammar string.  [static]
 *  \return empty grammar string.
*/
QString C_PosologieGrammar::empty_skeleton_grammar()
{return QString(
                "<gph>\n"
                " <gph_id></gph_id>\n"             // cip
                " <gph_pk></gph_pk>\n"             // pk du favori
                " <gph_uf></gph_uf>\n"             // forme de l'unite de prise
                " <gph_voie></gph_voie>\n"         // voie d'administration
                " <gph_fmin></gph_fmin>\n"         // frequence minimum
                " <gph_fmax></gph_fmax>\n"         // frequence maximum
                " <gph_funit></gph_funit>\n"       // frequence unite
                " <gph_pmint></gph_pmin>\n"        // posologie minimum
                " <gph_pmax></gph_pmax>\n"         // posologie maximum
                " <gph_punit></gph_punit>\n"       // posologie unite
                " <gph_pfc></gph_pfc>\n"           // posologie facteur corporel qu de gph_punit par facteur corporel
                " <gph_pfcunit></gph_pfcunit>\n"   // posologie facteur corporel unite Kg cm
                " <gph_pqbyuf></gph_pqbyuf>\n"     // quantite d'unite posologique par unite de forme de gph_punit par gph_uf
                " <gph_dmin></gph_dmin>\n"         // duree minimum
                " <gph_dmax></gph_dmax>\n"         // duree maximum
                " <gph_dunit></gph_dunit>\n"       // duree unite
                " <gph_cm></gph_cm>\n"             // conversion mode  UP_QUANTITE MOMENT_MEAL ALL_LITERAL
                " <gph_in>\n"                      // liste des indications
                " </gph_in>"                       // fin de la liste des indications <gph_il> </gph_il>
                " <gph_co></gph_co>\n"
                "</gph>\n"
                "<gps>\n"
                " <gpp></gpp>\n"
                "</gps>\n");
}
//---------------------------- setDrugRoute [static] ------------------------------------------------
/*! \brief set the voie form string in posologie grammar.  [static]
 *  \param const QString &grammar_expression :     grammar input string which must be modified
 *  \param const QString &drugRoute :     drug route to set
 *  \return modified grammar expression.
*/
QString C_PosologieGrammar::setDrugRoute(QString &grammar_expression, const QString &drugRoute)
{ CGestIni::setXmlData("gph_voie", drugRoute.toAscii(),  grammar_expression);
  return   grammar_expression;
}

//---------------------------- drugRoute [static] ------------------------------------------------
/*! \brief return drugRoute form string from posologie grammar. If not present return "" [static]
 *  \param const QString &grammar_expression :     grammar input string
 *  \return the drug route if present.
 */
QString C_PosologieGrammar::drugRoute(const QString &grammar_expression)
{return   CGestIni::getXmlData("gph_voie", grammar_expression).remove('\n').trimmed();
}

//---------------------------- setUnitPriseForm [static] ------------------------------------------------
/*! \brief set the unit prise form string in posologie grammar.  [static]
 *  \param const QString &grammar_expression :     grammar input string which must be modified
 *  \param const QString &unitUPForm :     unit prise form to set
 *  \return modified grammar expression.
*/
QString C_PosologieGrammar::setUnitPriseForm(QString &grammar_expression, const QString &unitUPForm)
{ CGestIni::setXmlData("gph_uf", unitUPForm.toAscii(),  grammar_expression);
  return   grammar_expression;
}

//---------------------------- unitPriseForm [static] ------------------------------------------------
/*! \brief return the unit prise form string from posologie grammar. If not present return "" [static]
 *  \param const QString &grammar_expression :     grammar input string as
 *  \return the unit prise form if present.
 */
QString C_PosologieGrammar::unitPriseForm(const QString &grammar_expression)
{return   CGestIni::getXmlData("gph_uf", grammar_expression).remove('\n').trimmed();
}
//---------------------------- setCip [static] ------------------------------------------------
/*! \brief set cip code in posologie grammar.  [static]
 *  \param const QString &grammar_expression :     grammar input string which must be modified
 *  \param const QString &cip :     cip code to set
 *  \return modified grammar expression.
*/
QString C_PosologieGrammar::setCip(QString &grammar_expression, const QString &cip)
{ CGestIni::setXmlData("gph_id", cip.toAscii(),  grammar_expression);
  return   grammar_expression;
}

//---------------------------- cip [static] ------------------------------------------------
/*! \brief return the cip code string from posologie grammar. If not present return "" [static]
 *  \param const QString &grammar_expression :     grammar input string as
 *  \return cip code if present.
*/
QString C_PosologieGrammar::cip (const QString &grammar_expression)
{QString id        = CGestIni::getXmlData("gph_id", grammar_expression).remove('\n').trimmed();
 return id;
}
//---------------------------- get_cip_pk [static] ------------------------------------------------
/*! \brief return cip code string from posologie grammar. If not present return ""
 *  \param const QString &grammar_expression :     grammar input string as
 *  \param QString *pk :  if not zero pk code will be returned in the contents of this QString pointer.
 *  \return cip code if present.
*/
QString C_PosologieGrammar::get_cip_pk(const QString &grammar_expression, QString *_pk /* = 0 */)
{int nextPos       = 0;
 QString id        = CGestIni::getXmlData("gph_id", grammar_expression, &nextPos).remove('\n').trimmed();
 QString pk        = CGestIni::getXmlData("gph_pk", grammar_expression, &nextPos).remove('\n').trimmed();
 if (_pk) *_pk = pk;
 return id;
}
//---------------------------- get_indicationsList [static] ------------------------------------------------
/*! \brief return indication list from grammar string [static]
 *  \param const QString &grammar_expression :     grammar input string as
 *  \return list code if present.
*/
QStringList C_PosologieGrammar::get_indicationsList(const QString &grammar_expression)
{QStringList list  = CGestIni::getXmlDataList("gph_il", grammar_expression);
 return list;
}

//---------------------------- get_indication [static] ------------------------------------------------
/*! \brief return indication from posologie grammar.  [static]
 *  \param const QString &grammar_expression :     grammar input string which must be modified
 *  \param , int index :     index of indication to retrieve
 *  \return indication expression 'code'|'typCode'|'libelle'
*/
QString C_PosologieGrammar::get_indication(const QString &grammar_expression, int index, int flag /*= C_PosologieGrammar::ALL_INDIC */)
{ QStringList list  = CGestIni::getXmlDataList("gph_il", grammar_expression);
  int nb = list.count();
  if (nb==0) return "";
  QString indication = list[qMin( nb-1, index)];
  list        = indication.split('|');
  indication  = "";
  nb          = list.count();
  if ( nb>=1 && flag & C_PosologieGrammar::CODE)      indication += list[0]+"|";
  if ( nb>=2 && flag & C_PosologieGrammar::CODE_TYPE) indication += list[1]+"|";
  if ( nb>=3 && flag & C_PosologieGrammar::LIBELLE)   indication += list[2]+"|";
  if (indication.endsWith('|')) indication = indication.left(indication.length()-1);
  return indication;
}

//---------------------------- toString ------------------------------------------------
QString C_PosologieGrammar::toString(const QString &_up_forme /* = "" */)
{return toString(m_grammar_expression,  _up_forme, m_must_be_literal);
}
//---------------------------- toString [static] ------------------------------------------------

/*! \brief return human readable string from posologie grammar.
 *  \param const QString &grammar_expression     xml grammar input string as
 *      <gph>
 *         <gph_id>301546</gph_id>
 *         <gph_pk>123</gph_pk>
 *         <gph_uf>comprimé</gph_uf>
 *         <gph_cm>UP_QUANTITE DURATION</gph_cm>
 *         <gph_in>
 *             <gph_il>I13.1|CIM|insuffisance rénale et cardiopathie hypertensive</gph_il>
 *             <gph_il>P96.0|CIM|insuffisance rénale congénitale</gph_il>
 *         </gph_in>
 *         <gph_co>à prendre avec un grand verre d'eau sans respirer pendant 30 minutes</gph_co>
 *      </gph>
 *      <gps>
 *         <gpp>[1;m|2;d|4;s]J8</gpp>
 *         <gpp>[0.5;m|1;d15r|2;sr30|3;h10-13]J4\</gpp>
 *         <gpp>[0.5;m|1;s]J3</gpp>
 *      </gps>
 *  \param int must_be_literal  undefined if -1 (the grammar string try to be used for this and if not possible it's defined to C_PosologieGrammar::NOT_LITERAL)
 *         otherwise it's must be a combination of : C_PosologieGrammar::UP_QUANTITE
 *                                                   C_PosologieGrammar::UP_JUST_FRACTION   //  conversion uniquement de 0.25 en un quart 0.5 en un demi 0.75 en trois quart
 *                                                   C_PosologieGrammar::DURATION
 *                                                   C_PosologieGrammar::SEQUENCE_HOUR
 *                                                   C_PosologieGrammar::MOMENT_MEAL
 *                                                   C_PosologieGrammar::INDICATION_LIST   indications list must be showed
 *                                                   C_PosologieGrammar::INDICATION_CODE   indications code must be showed
 *                                                   C_PosologieGrammar::INDICATION_TYPE   indications code typ (CIP CISP etc.) must be showed
 *         this parameter define how numerics values must be converted in literal string.
 *  \param QString &_up_forme      if == "" the grammar string try to be used to define, this parameter is the drug unit form.
 *  \return a french human readable string (for other langs the source code is there after ...)
 */
//301546/123!ALL_LITERAL=comprimé@I13.1|CIP|insuffisance rénale et cardiopathie hypertensive@P96.0|CIP|insuffisance rénale congénitale
//301546/123!ALL_LITERAL=comprimé@I13.1|CIP|insuffisance rénale et cardiopathie hypertensive@P96.0|CIP|insuffisance rénale congénitale
//
QString C_PosologieGrammar::toString(const QString &grammar_expression, const QString &_up_forme, int must_be_literal /* = -1 */  )
{
 int nextPos       = 0;
 int pos           = grammar_expression.indexOf("<gps>"); if (pos==-1)     return QString("");
 QString header    = CGestIni::getXmlData("gph", grammar_expression, &nextPos);
 QString sequences = CGestIni::getXmlData("gps", grammar_expression, &nextPos).remove('\n').trimmed();
 nextPos           = 0;
 QString id        = CGestIni::getXmlData("gph_id", header, &nextPos).remove('\n').trimmed();
 QString pk        = CGestIni::getXmlData("gph_pk", header, &nextPos).remove('\n').trimmed();
 QString up_forme  = CGestIni::getXmlData("gph_uf", header, &nextPos).remove('\n').trimmed();
 QString conv_mod  = CGestIni::getXmlData("gph_cm", header, &nextPos).remove('\n').trimmed();
 QString indicStr  = CGestIni::getXmlData("gph_in", header, &nextPos).remove('\n').trimmed();
 QString comment   = CGestIni::getXmlData("gph_co", header, &nextPos).trimmed();

 //........ traiter le cas ou la definition de literalite est donnee dans la grammaire avant le signe = .........
 if (conv_mod.length())          // .......... si literalite definie dans grammaire l'utiliser que si parametre d'entree non defini ..............
    {if (must_be_literal==-1)    // si parametre indefini en entree alors utiliser la definition de la grammaire
        {must_be_literal = literalStringToFlag(conv_mod);
         /*
         must_be_literal = 0;
         if (conv_mod.contains("UP_QUANTITE"))        must_be_literal |= C_PosologieGrammar::UP_QUANTITE;
         if (conv_mod.contains("DURATION"))           must_be_literal |= C_PosologieGrammar::DURATION;
         if (conv_mod.contains("SEQUENCE_HOUR"))      must_be_literal |= C_PosologieGrammar::SEQUENCE_HOUR;
         if (conv_mod.contains("MOMENT_MEAL"))        must_be_literal |= C_PosologieGrammar::MOMENT_MEAL;
         if (conv_mod.contains("ALL_LITERAL"))        must_be_literal  = C_PosologieGrammar::ALL_LITERAL;
         if (conv_mod.contains("NOT_LITERAL"))        must_be_literal  = C_PosologieGrammar::UP_JUST_FRACTION;
         if (conv_mod.contains("INDICATION_LIST"))    must_be_literal |= C_PosologieGrammar::INDICATION_LIST;
         if (conv_mod.contains("INDICATION_CODE"))    must_be_literal |= C_PosologieGrammar::INDICATION_CODE;
         if (conv_mod.contains("INDICATION_TYPE"))    must_be_literal |= C_PosologieGrammar::INDICATION_TYPE;
         */
        }
    }
 if (must_be_literal==-1)
   {  must_be_literal = C_PosologieGrammar::NOT_LITERAL;
   }
 //............. si forme definie en entree on l'utilise .....................................
 //              sinon on tente d'utiliser celle définie dans la grammaire
 up_forme             = (_up_forme.length())?_up_forme:up_forme;
 nextPos              = 0;
 QString joinStr      = QObject::tr(", \npuis ");
 QString ret          = "";
 QStringList seqList  = CGestIni::getXmlDataList("gpp", sequences);
 for (int i=0; i<seqList.count(); ++i)
     {ret  += sequenceToString(seqList[i], must_be_literal, up_forme); ret += joinStr;
     }
 ret = ret.left(ret.length()-joinStr.length());  // on enleve le dernier "\npuis "
 return ret;
}
//---------------------------- sequenceToString ------------------------------------------------
// <gpp>[1;m|2;d|4;s]J8!à la phase initiale en bolus</gpp>
// <gpp>[100-200;]U1!à la phase initiale en bolus</gpp>   // 100 à 200 mg à ne faire q'une fois à la phase initiale en bolus
QString C_PosologieGrammar::sequenceToString(const QString &sequence, int must_be_literal /* = C_PosologieGrammar::NOT_LITERAL */, const QString &up_forme)
{int pos             = sequence.indexOf(']');
 if (pos==-1) return QString("");
 int pcom            = sequence.indexOf('!');
 int len             = -1;    // si pas de commentaire le mid prend tout
 QString commentaire = "";
 if (pcom != -1)
    { commentaire = sequence.mid(pcom+1);
      len = pcom-pos-1;
    }
 QString periodUnit = "";   // il y sera retoune l'unite de la periode (jour semaine mois an)
 QString ret        = "";
 QString seq_days   = sequence.mid(pos + 1,len);
 QString seq_time   = sequence.left(pos).remove("[").trimmed();
 ret               += sequence_timeToString(seq_time, must_be_literal, up_forme);
 ret               += sequence_daysToString(seq_days, periodUnit, must_be_literal );
 if (commentaire.length())
        ret        += " " + commentaire;
 return ret;
}

//---------------------------- sequence_daysToString ------------------------------------------------
//  J2
QString C_PosologieGrammar::sequence_daysToString(const QString &seq_days, QString &periodUnit, int must_be_literal /* = C_PosologieGrammar::NOT_LITERAL */)
{
 QString       t = seq_days.at(0);
 QString     ret = seq_days;
 if (t=="U")
     {QString str_nb = ret.mid(1);
      int         nb = str_nb.toInt();
      periodUnit     = QObject::tr("fois");
      ret            = QObject::tr("à ne faire ") +
                       ((nb>1)?QObject::tr("que "):"qu'") +
                       ( (must_be_literal&C_PosologieGrammar::DURATION)?numberToLiteral(nb,((nb>1)?0:102)):str_nb) + " " + periodUnit;
     }
 else if (t=="J")
    {QString str_nb = ret.mid(1);
     int         nb = str_nb.toInt();
     periodUnit     = QObject::tr("jour");
     ret            = QObject::tr("pendant ") + ( (must_be_literal&C_PosologieGrammar::DURATION)?numberToLiteral(nb):str_nb) +" " + periodUnit+ ((nb>1)?"s ":" ");
    }
 else if (t=="S")
    {QString str_nb = ret.mid(1);
     int         nb = str_nb.toInt();
     periodUnit     = QObject::tr("semaine");
     ret            = QObject::tr("pendant ") + ( (must_be_literal&C_PosologieGrammar::DURATION)?numberToLiteral(nb):str_nb) +" " + periodUnit+ ((nb>1)?"s ":" ");
    }
 else if (t=="M")
    {QString str_nb = ret.mid(1);
     int         nb = str_nb.toInt();
     periodUnit     = QObject::tr("mois");
     ret            = QObject::tr("pendant ") + ( (must_be_literal&C_PosologieGrammar::DURATION)?numberToLiteral(nb):str_nb) +" " + periodUnit;
    }
 else if (t=="A")
    {QString str_nb = ret.mid(1);
     int         nb = str_nb.toInt();
     periodUnit     = QObject::tr("an");
     ret            = QObject::tr("pendant ") + ( (must_be_literal&C_PosologieGrammar::DURATION)?numberToLiteral(nb):str_nb) +" " + periodUnit+ ((nb>1)?"s ":" ");
    }
 return ret;
}
//---------------------------- sequence_timeToString ------------------------------------------------
// 0.5;m|1;d15r|2;sr30|3;h10-13
QString C_PosologieGrammar::sequence_timeToString(const QString &seq_time, int must_be_literal /* = C_PosologieGrammar::NOT_LITERAL */, const QString &_up_forme /* = "" */)
{QString     up_forme = "";
 QStringList  seqList = seq_time.split('|');
 QString          ret = "";
 QString          str = "";
 QString           qu = "";
 QString          qu2 = "";
 QString       qu_lit = "";
 QString      qu_lit2 = "";
 QString           t  = "";
 QString before       = "";
 QString after        = "";
 int     before_n     = 0;
 int     after_n      = 0;
 int              pos = 0;
 int               p  = 0;
 int               nb = 0;
 int                l = 0;
 for (int i=0; i<seqList.count(); ++i)
    {up_forme   = _up_forme;   // ne pas accorder sexe nombre plusieurs fois donc reinitialiser
     str        = seqList[i];
     pos        = str.indexOf(';');
     if (pos   != -1)
        {//.......... C_PosologieGrammar::UP_QUANTITE la quantite d'unite de prise .....................
         //           ici est traite le mode literal ou numerique
         qu     = str.left(pos);
         p      = qu.indexOf('-');
         if (p != -1)                // cas ou 1-2   (un à deux)
            { qu2 = qu.mid(p+1);
              qu  = qu.left(p);
            }

         if      (must_be_literal & C_PosologieGrammar::UP_QUANTITE)       // tout doit etre converti en lettre quelque soit le flag C_PosologieGrammar::UP_JUST_FRACTION
                 {qu_lit = numberToLiteral(qu, up_forme, 0);               // cet appel est toujours necessaire meme si pas de literal car met a jour orthographe et pluriel de up_forme
                  ret   += qu_lit;
                  if (qu2.length())
                     {qu_lit2 = numberToLiteral(qu2, up_forme, 0);
                      ret    += QObject::tr(" à ") + qu_lit2;
                     }

                 }
         else if (must_be_literal & C_PosologieGrammar::UP_JUST_FRACTION)  // seules les parties fractionnaires 0.25 0.30 0.75 --> un quart un demi  trois quart
                 {qu_lit = numberToLiteral(qu, up_forme, 1);               // cet appel est toujours necessaire meme si pas de literal car met a jour orthographe et pluriel de up_forme
                  ret   += qu_lit;
                  if (qu2.length())
                     {qu_lit2 = numberToLiteral(qu2, up_forme, 1);
                      ret    += QObject::tr(" à ") + qu_lit2;
                     }
                 }
         else                                                              // tout doit rester en numerique
                 {qu_lit = numberToLiteral(qu, up_forme, 0);               // cet appel est toujours necessaire meme si pas de literal car met a jour orthographe et pluriel de up_forme
                  ret   += qu;
                  if (qu2.length())
                     {qu_lit2 = numberToLiteral(qu2, up_forme, 1);
                      ret    += QObject::tr(" à ") + qu2;
                     }
                 }
         //.......... la forme (precedemment ajustee grammaticalement par l'appel numberToLiteral(qu, up_forme);) ..............
         ret   += " " + up_forme+" "; // + ((qu.toFloat()>1.0)?"s ":" ");
         //........... C_PosologieGrammar::SEQUENCE_HOUR  le moment ou prendre les medocs dans la journee .......................
         //            si specifie en heures, le mode literal
         //            ou numerique sera traite ici
         str    = str.mid(pos+1);
         l      = str.length();
         if (l==0)             // mention l apres le point virgule
            {ret   += QObject::tr(" par jour");          // sera remplacé par l'unite de temps de la periode (jour semaine mois etc...)
            }
         else
            { t      = str.at(0);    // relever le m M a s c n h
              str    = str.mid(1);
                   if ( t=="J") ret += QObject::tr("par jour");
              else if ( t=="S") ret += QObject::tr("par semaine");
              else if ( t=="M") ret += QObject::tr("par mois");
              else if ( t=="A") ret += QObject::tr("par an");

              else if ( t=="m") ret += QObject::tr("le matin");
              else if ( t=="d") ret += QObject::tr("à midi");
              else if ( t=="a") ret += QObject::tr("l'après-midi");
              else if ( t=="s") ret += QObject::tr("le soir");
              else if ( t=="c") ret += QObject::tr("au coucher");
              else if ( t=="n") ret += QObject::tr("la nuit");
              else if ( t=="h")  // mode horaire
                      {pos  = str.indexOf('-');
                       nb   = 0;                   // sera utilise pour le pluriel des heures
                       if (pos != -1)
                          {      before   = str.left(pos);
                                 after    = str.mid(pos+1);
                                 before_n = before.toInt();
                                 after_n  = after.toInt();
                                 nb       = qMax(before_n,after_n);  // reperer si le pluriel doit etre applique
                                 if (must_be_literal & C_PosologieGrammar::SEQUENCE_HOUR)
                                    {before  =  numberToLiteral(before_n);
                                     after   =  numberToLiteral(after_n);
                                    }
                                 ret   += QObject::tr("de ")+ before + QObject::tr(" à ") + after;
                          }
                       else
                          {      nb = str.toInt();
                                 if (must_be_literal & C_PosologieGrammar::SEQUENCE_HOUR)
                                    { str = numberToLiteral(nb);
                                    }
                                 ret   += QObject::tr("à ") + str;
                          }
                       ret += QObject::tr(" heure") + ((nb>1)?"s":"");
                      }
              //............ C_PosologieGrammar::MOMENT_MEAL le moment par rapport au repas .........................
              //             en minutes, le mode literal numerique sera traite ici
              pos  = str.indexOf('r');
              if  (pos != -1)
                  { before   = str.left(pos);
                    after    = str.mid(pos+1);
                    before_n = before.toInt();
                    after_n  = after.toInt();
                    //if (before_n||after_n)
                       {
                        if (must_be_literal & C_PosologieGrammar::MOMENT_MEAL)
                           { before = numberToLiteral(before.toInt());
                             after  = numberToLiteral(after.toInt());
                           }
                        if      (before.length()) ret += " " + before + QObject::tr(" minutes avant le repas");
                        else if (after.length() ) ret += " " + after  + QObject::tr(" minutes après le repas");
                        else                      ret += " " + QObject::tr("au cours du repas");
                       }
                  }
          } // if (str.length())
         ret += ", ";
        }
    }  // end for (int i=0; i<seqList.count(); ++i)
 return ret;
}
//---------------------------- numberToLiteral ------------------------------------------------
QString C_PosologieGrammar::numberToLiteral(QString number, QString &up_forme , int justOneFraction /* = false */)
{number.replace(',','.');
 QString       str  = "";
 int left_part_int  = 0;
 QString left_part  = "";
 QString right_part = "";
 char          sexe = 'm';
 int pos            = number.indexOf(".");
 if (pos != -1)               // UP_QUANTITE et UP_JUST_FRACTION ==>
    {//  0.75 1.75 789.75
     left_part         = number.left(pos);
     right_part        = number.mid (pos+1);
     left_part_int     = left_part.toInt();
     up_forme          = adjustPlurielUpForme(left_part_int, up_forme, sexe);
     if (left_part_int==0)  left_part  = "";
     else                   left_part  = numberToLiteral(left_part.toInt(), sexe);
     if      (right_part=="25")
             { if (left_part_int)
                  {if (justOneFraction ) return number;
                   else                  return left_part + "et un quart de";
                  }
               else                      return QString("un quart de");
             }
     else if (right_part=="50" || right_part=="5")
             { str = (sexe=='f')?"une demie":"un demi";
               if (left_part_int)
                  {if (justOneFraction) return number;
                   else                 return left_part + "et "+ str;
                  }
               else                     return str;
             }
     else if (right_part=="75")
             { if (left_part_int)
                  {if (justOneFraction) return number;
                   else                 return left_part + "et trois quarts de";
                  }
               else                     return QString("trois quarts de");
             }
     else    { if (justOneFraction)     return number;
               else  right_part = numberToLiteral(right_part.toInt(),sexe);
             }
     if (left_part_int==0) return QString("zéro virgule ") + right_part;
     else                  return left_part + " virgule "  + right_part;
    }
 left_part_int     = number.toInt();
 up_forme          = adjustPlurielUpForme(left_part_int, up_forme, sexe);
 if (justOneFraction) return number;   // toujours executer adjustPlurielUpForme() car ajuste la forme au pluriel
 else                 return numberToLiteral(left_part_int,sexe);
}
//---------------------------- adjustPlurielUpForme ------------------------------------------------
QString C_PosologieGrammar::adjustPlurielUpForme(int nb, const QString &_up_forme, char &sexe)
{QString up_forme = _up_forme;
 sexe = 'm';
 int pos = -1;
 if ( (pos=up_forme.indexOf("cuillere"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("cuillere",(nb>1 && up_forme[pos+8] != 's')?"cuillères":"cuillère").replace(" a "," à ");
 }
 if ( (pos=up_forme.indexOf("cuillère"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("cuillère",(nb>1 && up_forme[pos+8] != 's')?"cuillères":"cuillère").replace(" a "," à ");
 }
 else if( (pos=up_forme.indexOf("application"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("application",(nb>1 && up_forme[pos+11] != 's')?"applications":"application");
 }
 else if( (pos=up_forme.indexOf("goutte"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("goutte",(nb>1 && up_forme[pos+6] != 's')?"gouttes":"goutte");
 }
 else if( (pos=up_forme.indexOf("injection"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("injection",(nb>1 && up_forme[pos+9] != 's')?"injections":"injection");
 }
 else if( (pos=up_forme.indexOf("inhalation"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("inhalation",(nb>1 && up_forme[pos+10] != 's')?"inhalations":"inhalation");
 }
 else if( (pos=up_forme.indexOf("gelule"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("gelule",(nb>1 && up_forme[pos+6] != 's')?"gélules":"gélule");
 }
 else if( (pos=up_forme.indexOf("gélule"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("gélule",(nb>1 && up_forme[pos+6] != 's')?"gélules":"gélule");
 }
 else if( (pos=up_forme.indexOf("pulverisation"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("pulverisation",(nb>1 && up_forme[pos+13] != 's')?"pulvérisations":"pulvérisation");
 }
 else if( (pos=up_forme.indexOf("pulvérisation"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("pulvérisation",(nb>1 && up_forme[pos+13] != 's')?"pulvérisations":"pulvérisation");
 }
 else if( (pos=up_forme.indexOf("ampoule"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("ampoule",(nb>1 && up_forme[pos+7] != 's')?"ampoules":"ampoule");
 }
 else if( (pos=up_forme.indexOf("capsule"))!=-1 )
 {sexe = 'f';
  return up_forme.replace("capsule",(nb>1 && up_forme[pos+7] != 's')?"capsules":"capsule");
 }
 else if( (pos=up_forme.indexOf("ml"))!=-1 )
 {sexe = 'm';
  return up_forme;
 }
 else if(up_forme.contains("mg"))
 {sexe = 'm';
  return up_forme;
 }
 else if(up_forme.contains("ui"))
 {sexe = 'f';
  return up_forme;
 }
 else if( (pos=up_forme.indexOf("comprime"))!=-1 )
 {sexe = 'm';
  return up_forme.replace("comprime",(nb>1 && up_forme[pos+8] != 's')?"comprimés":"comprimé");
 }
 else if( (pos=up_forme.indexOf("comprimé"))!=-1 )
 {sexe    = 'm';
  return up_forme.replace("comprimé",(nb>1 && up_forme[pos+8] != 's')?"comprimés":"comprimé");
 }

 return ((nb>1 && !up_forme.endsWith('s'))?up_forme+"s":up_forme);
}

//---------------------------- numberToLiteral ------------------------------------------------
QString C_PosologieGrammar::numberToLiteral(int nb, char sexe /* = 'm' */)
{
 QString ret = "";
 char nompart[10][10]={"dix","onze","douze","treize","quatorze","quinze","seize","dix-sept","dix-huit","dix-neuf"};
 char nomunite[10][10]={"","un","deux","trois","quatre","cinq","six","sept","huit","neuf"};
 char nomdizaine[8][17]={"vingt","trente","quarante","cinquante","soixante","soixante-","quatre-vingt","quatre-vingt-"};
 char nomspe[3][7]={"cent","-et-un","mille"};
 int milliers  = nb/1000;
     nb        = nb%1000;
 int centaines = nb/100;
 int unites    = nb%100;
 int dizaines  = unites/10;
 unites=unites%10;
 if (milliers>1 && milliers<10)       ret += adjustUniteToSexe(nomunite[milliers],sexe)  +" " + QString(nomspe[2])+" ";
 if (milliers==1)                     ret += QString(nomspe[2]) + " ";
 if (centaines>1)
    {                                 ret += adjustUniteToSexe(nomunite[centaines],sexe) +" " + QString(nomspe[0])+" ";
     if ( unites==0 && dizaines==0 )  ret  = ret.left(ret.length()-1);
    }
 if ( centaines==1 )                  ret += QString(nomspe[0]) + " ";
 if ( dizaines>1 && dizaines!=7 && dizaines!=9 )
    {                                 ret += QString(nomdizaine[dizaines-2]);
     if ( dizaines==8 && unites==0 )  ret += QString("s");
     if ( unites>1 )
        {                             ret += "-" + adjustUniteToSexe(nomunite[unites],sexe);
          unites=0;
        }
     if ( unites==1 )
        { unites=0;                   ret += adjustUniteToSexe(nomspe[1],sexe);
        }
    }
 if ( dizaines==1 || dizaines==7 || dizaines==9 )
    {
     if (dizaines==7 || dizaines==9)  ret += QString(nomdizaine[dizaines-2]);
     if (dizaines==7 && unites==1)
        {ret.left(ret.length()-1);    ret += adjustUniteToSexe(nomspe[1],sexe);
         ret.left(ret.length()-2);
        }
                                      ret += QString(nompart[unites]);
     unites=0;
    }
 if (dizaines!=0 && centaines!=0)     ret += " ";
                                      ret += adjustUniteToSexe(nomunite[unites],sexe);
 return ret.trimmed();
}
//---------------------------- adjustUniteToSexe ------------------------------------------------
QString C_PosologieGrammar::adjustUniteToSexe(const QString &unite, char sexe )
{if (unite.contains("un") && sexe=='f') return QString(unite).replace("un","une");
 return unite;
}
