/*
    Fichier cylcree.c
    Auteur Bernard Chardonneau

    Logiciel libre, droits d'utilisation précisés en français
    dans le fichier : licence-fr.txt

    Traductions des droits d'utilisation dans les fichiers :
    licence-en.txt , licence-es.txt , licence-pt.txt ,
    licence-eo.txt , licence-eo-utf.txt

    Droits d'utilisation également sur la page web :
    http://cyloop.tuxfamily.org/voir.php?page=droits


    Programme permettant de créer un fichier cyloop.
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "types.h"
#include "limites.h"
#include "messages.h"
#include "dialogue.h"

#define  szmax_nomfic   80


// prototypes
char *recup_nomfic (char *nom);
void choix_cycle ();
void recup_info_var ();
void recap_duree ();
void gendon ();
void gendon16bits ();
void gendon32bits ();
void gendonfloat  ();



// variables globales (pour éviter des tas de passages de paramètre)
FILE        *descfic;  // descripteur du fichier cyloop
desc_cyloop entetefic; // entête du fichier cyloop
desc_var    entetevar; // entête de la variable du fichier cyloop



// programme principal

main (int narg, char *varg[])
{
    char *nomfic;  // nom du fichier cyloop


    // mémorisation du nom de la commande
    memcom (*varg);

    // récupération du nom de fichier et rajout éventuel du suffixe
    if (! --narg)
        nomfic = recup_nomfic (NULL);
    else
        nomfic = recup_nomfic (varg [1]);

    // terminé si aucun nom de fichier saisi (juste l'extention .cyl)
    if (strlen (nomfic) < 5)
        return;

    // on n'écrase pas un fichier cyloop qui existe déjà
    if (access (nomfic, 0) == 0)
        // "fichier cyloop %s déjà existant"
        err_fatale_arg ("CYLOOP_EXISTE", nomfic);

    // création du fichier cyloop
    descfic = fopen (nomfic, "w");

    if (! descfic)
        // "Création impossible du fichier cyloop %s"
        err_fatale_arg ("PB_CREE_CYLOOP", nomfic);

    // initialisation
    entetefic.signature [0] = 'c';  // signature du fichier cyloop
    entetefic.signature [1] = 'y';
    entetefic.signature [2] = 'l';
    entetefic.nb_var = 0;  // fichier cyloop de niveau 0 : une variable sans nom

    // choix du cycle de mesures
    choix_cycle ();

    // caractéristiques de la variable mémorisant les cycles
    recup_info_var ();

    // enregistrement de l'entête du fichier cyloop
    // on ne recopie pas les éventuels octets redondants de la structure
    fwrite (&entetefic, 16, 1, descfic);

    // enregistrement de l'entête de la variable du fichier cyloop
    if (entetevar.typedon & cmoy_sep)
        // une valeur de coef_moy pour chaque instant du cycle
        fwrite (&entetevar, 6, 1, descfic);
    else
        // champ coef_moy global pour la variable
        fwrite (&entetevar, 8, 1, descfic);

    // génération des données avec leur valeur initiale
    gendon ();

    // le fichier cyloop est prêt
    if (fclose (descfic) < 0)
        // "Problème d'écriture dans le fichier cyloop"
        affiche_err ("PB_ECR_CYLOOP");
}



// récupération du nom du fichier cyloop et rajout éventuel de son suffixe

char *recup_nomfic (char *nom)
{
    char  *nomfic;
    int   lg_nomfic;


    // si un nom de fichier a été passé en paramètre
    if (nom)
    {
        // récupérer sa taille
        lg_nomfic = strlen (nom);

        // si ce nom se termine par le suffixe .cyl
        if ((lg_nomfic > 4) && (strcmp (nom + lg_nomfic - 4, ".cyl") == 0))
        {
            // mémoriser le nom tel quel
            nomfic = malloc (lg_nomfic + 1);
            strcpy (nomfic, nom);
        }
        // sinon
        else
        {
            // rajouter au nom le suffixe .cyl et mémoriser le résultat
            nomfic = malloc (lg_nomfic + 5);
            strcpy (nomfic, nom);
            strcpy (nomfic + lg_nomfic, ".cyl");
        }
    }
    // sinon
    else
    {
        // demander le nom du fichier à l'opérateur et mémoriser sa réponse
        nomfic = malloc (szmax_nomfic);

        // "Nom du fichier ? "
        affiche_msg ("DEMANDE_NOMFICH");
        fgets (nomfic, szmax_nomfic - 4, stdin);

        // supprimer le '\n' éventuel retourné par la fonction fgets
        lg_nomfic = strlen (nomfic);

        if (nomfic [lg_nomfic - 1] == '\n')
            nomfic [--lg_nomfic] = '\0';

        // rajouter au nom le suffixe .cyl si nécessaire
        if ((lg_nomfic < 4) || (strcmp (nomfic + lg_nomfic - 4, ".cyl") != 0))
            strcpy (nomfic + lg_nomfic, ".cyl");

        // vider le buffer clavier
        fflush (stdin);
    }

    // retourner le nom du fichier
    return nomfic;
}



// choix du cycle de mesures

void choix_cycle ()
{
    struct tm stm;      // structure contenant la date et l'heure
    uint32 timep;       // date et heure dans un uint32
    int h_ete;          // mémorise si heure d'été en ce moment
    int choix, utcloc;  // choix de l'opérateur
    uint32 duree;       // duree d'un cycle


    // initialisation champs caractéristiques et état
    entetefic.caract_gen  = 0;
    entetefic.etat_cyloop = 0;

    // choix du cycle
    // "Choisissez un cycle de mesure\n"
    affiche_ligne ("CHOIX_CYCLEMES");

            // "Cycle annuel débutant le 1er janvier à 0 H"
            // "Cycle mensuel débutant le 1er jour du mois à 0 H"
            // "Cycle hebdomadaire débutant le lundi à 0 H"
            // "Cycle hebdomadaire débutant le dimanche à 0 H"
            // "Cycle journalier débutant à 0 H"
            // "Autre cycle"
    choix = lecchoix ("CHOIX_CYCLEMES1", "CHOIX_CYCLEMES2",
                      "CHOIX_CYCLEMES3", "CHOIX_CYCLEMES4",
                      "CHOIX_CYCLEMES5", "CHOIX_CYCLEMES6", NULL);

    if (choix < 6)
    {
        // choisir le type d'heure
                           // "utilisation de l'heure temps universel"
                           // "utilisation de l'heure locale"
        utcloc = lecchoix ("UTIL_HEURE_TU", "UTIL_HEURE_LOC", NULL);

        if (utcloc & 1)
            entetefic.caract_gen |= temps_utc;

        // récupérer la date et l'heure courante
        time (&timep);

        // passer à 0 heures + récupérer date et heure dans structure
        if (entetefic.caract_gen & temps_utc)
        {
            // Passage à 0H par calcul si heure UTC
            timep = (timep / secjour) * secjour;

            // on récupère la structure ensuite
            gmtime_r (&timep, &stm);
        }
        else
        {
            // si heure locale on récupère d'abord la structure
            localtime_r (&timep, &stm);

            // mémorise si on est à l'heure d'été
            h_ete = stm.tm_isdst;

            // et on modifie l'heure qu'elle contient
            stm.tm_hour = 0;
            stm.tm_min = 0;
            stm.tm_sec = 0;

            // avant de recalculer l'heure du début de la journée
            timep = mktime (&stm);
        }

        switch (choix)
        {
            // Cycle annuel débutant le 1er janvier à 0 H
            case 1: entetefic.caract_gen |= cycle_ireg | cycle_annuel;
                    timep -= stm.tm_yday * secjour;

                    if (stm.tm_year % 4)
                        entetefic.duree_cycle = 365 * secjour;
                    else
                        entetefic.duree_cycle = 366 * secjour;

                    break;

            // Cycle mensuel débutant le 1er jour du mois à 0 H
            case 2: entetefic.caract_gen |= cycle_ireg;
                    timep -= (stm.tm_mday -1) * secjour;

                    // la durée du mois pourrait aussi être obtenue par calcul
                    switch (stm.tm_mon + 1)
                    {
                                 // février
                        case  2: if (stm.tm_year % 4)
                                     entetefic.duree_cycle = 28 * secjour;
                                 else
                                     entetefic.duree_cycle = 29 * secjour;

                                 break;

                        case  4: // mois de 30 jours
                        case  6:
                        case  9:
                        case 11: entetefic.duree_cycle = 30 * secjour;
                                 break;

                                 // mois de 31 jours (7 mois par an)
                        default: entetefic.duree_cycle = 31 * secjour;
                    }

                    break;

            // Cycle hebdomadaire débutant le lundi à 0 H
            case 3: if (stm.tm_wday)
                        timep -= (stm.tm_wday - 1) * secjour;
                    else
                        timep -= 6 * secjour;

                    entetefic.duree_cycle = 7 * secjour;
                    break;

            // Cycle hebdomadaire débutant le dimanche à 0 H
            case 4: timep -= stm.tm_wday * secjour;
                    entetefic.duree_cycle = 7 * secjour;
                    break;

            // Cycle journalier débutant à 0 H
            case 5: entetefic.duree_cycle = secjour;
        }

        entetefic.deb_cycle = timep;

        if (!(entetefic.caract_gen & temps_utc) && h_ete)
            entetefic.etat_cyloop |= heure_ete;
    }
    else
    {
        // choix de la durée du cycle
                // "Cycle de ... jours"
                // "Cycle de ... heures"
                // "Cycle de ... minutes"
                // "Cycle de ... secondes"
        choix = lecchoix ("CYCLE_EN_JOURS", "CYCLE_EN_HEURES",
                          "CYCLE_EN_MIN", "CYCLE_EN_SEC", NULL);

        // "Durée du cycle ? "
        affiche_msg ("DEM_DUREE_CYCLE");
        duree = lirentier ();

        switch (choix)
        {
            case 1: duree = duree * 24;
            case 2: duree = duree * 60;
            case 3: duree = duree * 60;
            case 4: entetefic.duree_cycle = duree;
        }

        // choix de l'instant de début du cycle
        // "Début du cycle\n"
        affiche_ligne ("DEM_DEBUT_CYCLE");

                // "Il y a ... jours"
                // "Aujourd'hui à 0 heures"
                // "Il y a ... heures"
                // "Au début de l'heure"
                // "Il y a ... minutes"
                // "Au début de la minute"
                // "Maintenant"
        choix = lecchoix ("DEM_DEB_CYCLE1", "DEM_DEB_CYCLE2",
                          "DEM_DEB_CYCLE3", "DEM_DEB_CYCLE4",
                          "DEM_DEB_CYCLE5", "DEM_DEB_CYCLE6",
                          "DEM_DEB_CYCLE7", NULL);

        if (choix == 1 || choix == 3 || choix == 5)
        {
            // "Depuis combien "
            affiche_msg ("DEPUIS_COMBIEN");

            switch (choix)
            {
                         // "de jours ? "
                case 1 : affiche_msg ("COMBIEN_JOURS");
                         break;

                         // "d'heures ? "
                case 3 : affiche_msg ("COMBIEN_HEURES");
                         break;

                         // "de minutes ? "
                case 5 : affiche_msg ("COMBIEN_MINUTES");
            }

            duree = lirentier ();
        }
        else
            duree = 0;

        // si démarrage calé sur un début de journée
        if (choix <= 2)
        {
            // choisir le type d'heure
                     // "utilisation de l'heure temps universel"
                     // "utilisation de l'heure locale"
            utcloc = lecchoix ("UTIL_HEURE_TU", "UTIL_HEURE_LOC", NULL);

            if (utcloc & 1)
                entetefic.caract_gen |= temps_utc;
        }
        else
            utcloc = 1;  // pas de distinction entre heure d'été et
                          // d'hiver pour les cycles de moins d'une journée

        // récupérer la date et l'heure courante
        time (&timep);

        // calculer l'innstant de début du cycle
        switch (choix)
        {
            case 1: // au début de la journée ou il y a duree jours
            case 2: if (utcloc)
                        // simple calcul si heure UTC
                        entetefic.deb_cycle =
                                        ((timep / secjour) - duree) * secjour;
                    else
                    {
                        // sinon, on soustraira l'hure locale
                        localtime_r (&timep, &stm);
                        timep = ((timep / 3600) - stm.tm_hour) * 3600;

                        // avant de remonter de duree jours
                        entetefic.deb_cycle = timep - (duree * secjour);

                        // mémorise également si on est à l'heure d'été
                        if (stm.tm_isdst)
                            entetefic.etat_cyloop |= heure_ete;
                    }
                    break;

                    // au début de l'heure
            case 3: entetefic.deb_cycle = (timep / 3600) * 3600;
                    break;

                    // il y a duree heures
            case 4: entetefic.deb_cycle = ((timep / 3600) - duree) * 3600;
                    break;

                    // au début de la minute
            case 5: entetefic.deb_cycle = (timep / 60) * 60;
                    break;

                    // il y a duree minutes
            case 6: entetefic.deb_cycle = ((timep / 60) - duree) * 60;
                    break;

                    // maintenant
            case 7: entetefic.deb_cycle = timep;

        }
    }

    // récapitulatif durée d'un cycle
    recap_duree ();

    // Saisie du nombre de données par cycle de mesure
    do
    {
        // "\nNombre de données par cycle de mesure ? "
        affiche_msg ("DEM_NB_DON_CYCLE");
        entetefic.nb_don_cycle = lirentier ();

        if (!entetefic.nb_don_cycle)
            // "Erreur de saisie, recommencez"
            affiche_err ("ERREUR_SAISIE");
    }
    while (!entetefic.nb_don_cycle);
}



// récapitulatif durée d'un cycle

void recap_duree ()
{
    int jour, heure, minute; // détail durée d'un cycle


    // "\nUn cycle de mesures "
    affiche_msg ("UN_CYCLE_MESURE");

    if (entetefic.caract_gen & cycle_ireg)
    {
        // "peut durer jusqu'à "
        affiche_msg ("PEUT_DURER");

        if (entetefic.caract_gen & cycle_annuel)
            // "366 jours"
            affiche_msg ("366_JOURS");
        else
            // "31 jours"
            affiche_msg ("31_JOURS");
    }
    else
    {
        // "dure"
        affiche_msg ("CYCLE_DURE");

        jour = entetefic.duree_cycle / secjour;
        heure = (entetefic.duree_cycle % secjour) / 3600;
        minute = (entetefic.duree_cycle % 3600) / 60;

        if (jour)
        {
            printf (" %d ", jour);

            if (jour > 1)
                // "jours"
                affiche_msg ("JOURS");
            else
                // "jour"
                affiche_msg ("JOUR");
        }

        if (heure)
        {
            printf (" %d ", heure);

            if (heure > 1)
                // "heures"
                affiche_msg ("HEURES");
            else
                // "heure"
                affiche_msg ("HEURE");
        }

        if (minute)
        {
            printf (" %d ", minute);

            if (minute > 1)
                // "minutes"
                affiche_msg ("MINUTES");
            else
                // "minute"
                affiche_msg ("MINUTE");
        }
    }

    putchar ('\n');
}



// caractéristiques de la variable mémorisant les cycles

void recup_info_var ()
{
    int choix;
    int valpond;


    // initialisations
    entetevar.der_enreg = 0;
    entetevar.typedon   = 0;
    entetevar.extention = 0;

    // "\nNature des données mémorisées\n"
    // "Compteur de passages", "Valeurs"
    affiche_ligne ("NATURE_DONNEES");
    choix = lecchoix ("COMPTEUR_PASSAGE", "MEMO_VALEURS", NULL);

    if (choix == 1)
        entetevar.typedon |= compteur;

    // "\nVoulez vous faire un calcul de pondération"
    // "à chaque changement de cycle ? "
    affiche_ligne ("DEM_CALC_PONDER");
    affiche_msg ("DEM_CALC_PONDER2");

    if (lirep () != 'n')
    {
        // "\nCoefficient de pondération ? "
        affiche_msg ("SAIS_COEF_PONDER");
        valpond = liponder ();

        while (valpond < 0 || valpond > 256)
        {
            // "Coefficient de pondération incorrect"
            // "Valeurs autorisées de 0,0039 à 0,996"
            // "ou de 0,39 % à 99,61 %"
            // "ou de 1/256 à 255/256"
            affiche_err ("ERREUR_COEF_POND");
            affiche_err ("VAL_PONDER_OK1");
            affiche_err ("VAL_PONDER_OK2");
            affiche_err ("VAL_PONDER_OK3");

            valpond = liponder ();
        }

        // la pondération minimale est de 1/256
        if (! valpond)
            valpond = 1;

        entetevar.ponderation = valpond;
    }
    else
        entetevar.ponderation = 0;

    if (entetevar.ponderation)
    {
        // "\nDoit on faire un calcul de pondération pour les cycles vides ? "
        affiche_msg ("PONDER_CYCLE_VIDE");

        if (lirep () != 'n')
            entetevar.typedon |= cpt_cycle_vide;
    }

    // "\nPour les calculs de moyenne, voulez-vous\n"
    affiche_ligne ("CALCULS_MOYENNE");

    if (entetevar.typedon & compteur)
        // "un coefficient global (conseillé)"
        // "un coefficient distinct pour chaque instant du cycle"
        choix = lecchoix ("COEF_GLOBAL_CONS", "COEF_DISTINCT", NULL);
    else
        // "un coefficient global pour tout le cycle"
        // "un coefficient distinct pour chaque instant du cycle"
        choix = lecchoix ("COEF_GLOBAL", "COEF_DISTINCT", NULL);

    if (choix == 1)
        entetevar.coef_moy = 0;
    else
        entetevar.typedon |= cmoy_sep;

    // "\nDurant un cycle, chaque instant peut il être pris en compte\n"
            // "une fois", "plusieurs fois"
    affiche_ligne ("CPT_INSTANTS");
    choix = lecchoix ("UNE_FOIS", "PLUSIEURS_FOIS", NULL);

    if (choix == 2)
        entetevar.typedon |= cumul_donnees;

    if ((entetevar.typedon & cmoy_sep) &&
        (entetevar.typedon & cumul_donnees))
    {
        // "\nLe coefficient de calcul de la moyenne doit il être mis à jour\n"
                // "une fois pour chaque instant du cycle"
                // "autant de fois que l'est la variable mémorisée"
        affiche_ligne ("MAJ_COEF_MOY");
        choix = lecchoix ("UNE_FOIS_INSTANT", "AUTANT_FOIS_VAR", NULL);

        if (choix == 2)
            entetevar.typedon |= cpt_toute_don;
    }

    // "\nFormat des données mémorisées\n"
    affiche_ligne ("DEM_FORM_DONNEES");

    // si la variable est un compteur
    if (entetevar.typedon & compteur)
    {
        // choix de la taille des données mémorisées
        if (entetevar.typedon & cumul_donnees)
            // "entier 16 bits", "entier 32 bits"
            choix = lecchoix ("ENTIER_16_BITS", "ENTIER_32_BITS", NULL);
        else
            // "entier 16 bits (suffisant)", "entier 32 bits"
            choix = lecchoix ("ENTIER_16_B_SUF", "ENTIER_32_BITS", NULL);

        if (choix == 2)
            entetevar.typedon |= donnees_32bits;
    }
    // sinon la variable mémorise une valeur à chaque instant des cycles
    else
    {
        // choix de la nature des données mémorisées
        // "entier 32 bits", "nombre flottant"
        choix = lecchoix ("ENTIER_32_BITS", "NOMBRE_FLOTTANT", NULL);

        entetevar.typedon |= donnees_32bits;

        if (choix == 2)
            entetevar.typedon |= val_float;

        // mémorisation des valeurs min et max des données ?
        // "\nVoulez vous mémoriser les valeurs"
        // "min et max de chaque instant du cycle ? "
        affiche_ligne ("MEM_MIN_MAX_1");
        affiche_msg ("MEM_MIN_MAX_2");

        if (lirep () != 'n')
            entetevar.typedon |= mem_min_max;
    }
}



// génération des données avec leur valeur initiale

void gendon ()
{
    // on appele une fonction dépendant du format des données

    if (entetevar.typedon & compteur)
    {
        if (entetevar.typedon & donnees_32bits)
            gendon32bits ();
        else
            gendon16bits ();
    }
    else
    {
        if (entetevar.typedon & val_float)
            gendonfloat ();
        else
            gendon32bits ();
    }
}



// génération des données d'une variable 16 bits avec leur valeur initiale

void gendon16bits ()
{
    // la structure de donnée maximale pour une variable 16 bits
    struct
    {
        uint16 coef_moy;
        int16  total;
    } donnees;

    int nb_octets; // nombre d'octets utilisés dans la structure ci-dessus
    void *debdon;  // adresse de début des données recopiées dans le fichier
    int i;         // compteur de boucle


    // initialiser les champs de la structure
    // et calculer debdon et nb_octets

    if (entetevar.typedon & cmoy_sep)
    {
        donnees.coef_moy = 0;
        debdon = &donnees;
        nb_octets = 4;
    }
    else
    {
        debdon = &donnees.total;
        nb_octets = 2;
    }

    donnees.total = 0;

    // recopier les données dans le fichier cyloop
    for (i = 0; i < entetefic.nb_don_cycle; i++)
        if (! fwrite (debdon, nb_octets, 1, descfic))
            // "Problème d'écriture dans le fichier cyloop"
            err_fatale ("PB_ECR_CYLOOP");
}



// génération des données d'une variable 32 bits avec leur valeur initiale

void gendon32bits ()
{
    // la structure de donnée maximale pour une variable 32 bits
    struct
    {
        uint16 vide;   // sinon coef_moy serait au mauvais endroit
        uint16 coef_moy;
        int32  total;
        int32  min;
        int32  max;
    } donnees;

    int nb_octets; // nombre d'octets utilisés dans la structure ci-dessus
    void *debdon;  // adresse de début des données recopiées dans le fichier
    int i;         // compteur de boucle


    // initialiser les champs de la structure
    // et calculer debdon et nb_octets

    if (entetevar.typedon & cmoy_sep)
    {
        donnees.coef_moy = 0;
        debdon = &donnees.coef_moy;
        nb_octets = 6;
    }
    else
    {
        debdon = &donnees.total;
        nb_octets = 4;
    }

    donnees.total = 0;

    if (entetevar.typedon & mem_min_max)
    {
        donnees.min = max_int32;
        donnees.max = min_int32;
        nb_octets = nb_octets + 8;
    }

    // recopier les données dans le fichier cyloop
    for (i = 0; i < entetefic.nb_don_cycle; i++)
        if (! fwrite (debdon, nb_octets, 1, descfic))
            // "Problème d'écriture dans le fichier cyloop"
            err_fatale ("PB_ECR_CYLOOP");
}



// génération des données d'une variable non entière avec leur valeur initiale

void gendonfloat ()
{
    // la structure de donnée maximale pour une variable 32 bits
    struct
    {
        uint16 vide;   // sinon coef_moy serait au mauvais endroit
        uint16 coef_moy;
        float  total;
        float  min;
        float  max;
    } donnees;

    int nb_octets; // nombre d'octets utilisés dans la structure ci-dessus
    void *debdon;  // adresse de début des données recopiées dans le fichier
    int i;         // compteur de boucle


    // initialiser les champs de la structure
    // et calculer debdon et nb_octets

    if (entetevar.typedon & cmoy_sep)
    {
        donnees.coef_moy = 0;
        debdon = &donnees.coef_moy;
        nb_octets = 6;
    }
    else
    {
        debdon = &donnees.total;
        nb_octets = 4;
    }

    donnees.total = 0;

    if (entetevar.typedon & mem_min_max)
    {
        donnees.min = max_float;
        donnees.max = min_float;
        nb_octets = nb_octets + 8;
    }

    // recopier les données dans le fichier cyloop
    for (i = 0; i < entetefic.nb_don_cycle; i++)
        if (! fwrite (debdon, nb_octets, 1, descfic))
            // "Problème d'écriture dans le fichier cyloop"
            err_fatale ("PB_ECR_CYLOOP");
}