/*
    Fichier cyldump.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 qui affiche la structure et le contenu d'un
    fichier cyloop.
*/


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

#define  szmax_nomfic   80


// prototypes
char *recup_nomfic (char *nom);
void dump_entetefic ();
void affnombre (int valeur, char *unite_singulier, char *unite_pluriel);
void dump_entetevar ();
void dumpdon ();
void dumpdon_uint16 ();
void dumpdon_uint32 ();
void dumpdon_int32 ();
void dumpdon_float ();



// 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]);

    // tentative de lecture du fichier cyloop
    descfic = fopen (nomfic, "r");

    if (! descfic)
        // "Fichier_cyloop %s inexistant ou protégé en lecture"
        err_fatale_arg ("CYLOOP_ABSENT", nomfic);

    // lecture de l'entête du fichier cyloop
    fread (&entetefic, 16, 1, descfic);

    // vérification de la signature
    if (entetefic.signature [0] != 'c' || entetefic.signature [1] != 'y'
                                       || entetefic.signature [2] != 'l')
    {
        // "Le fichier %s n'est pas un fichier_cyloop"
        aff_err_arg ("FICH_NON_CYLOOP", nomfic);
        fclose (descfic);
        return;
    }

    // on peut commencer le dump du fichier
    // "Dump du fichier cyloop %s\n\n"
    printf (message ("DUMP_CYLOOP"), nomfic);

    // dump de la description du fichier
    dump_entetefic ();

    // vérification de la valeur nb_var
    if (entetefic.nb_var)
    {
        // "Ce fichier cyloop utilise une liste de variables"
        // "utiliser la version complète de cyloop"
        affiche_err ("CYLOOP_LISTEVAR");
        affiche_err ("UTIL_VCOMPLETE");

        fclose (descfic);
        return;
    }

    // dump de la description de la variable
    dump_entetevar ();

    putchar ('\n');

    // dump des valeurs mémorisées dans la variable
    dumpdon ();

    // le fichier cyloop a été dumpé
    fclose (descfic);
}



// 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;
}



// dump de la description du fichier

void dump_entetefic ()
{
    struct tm stm; // structure contenant la date et l'heure
    int jour, heure, minute; // décomposition de la durée d'un cycle


    // "Début du cycle actuel"
    affiche_ligne ("DEBUT_CYCLE");

    if (entetefic.caract_gen & temps_utc)
        gmtime_r (&entetefic.deb_cycle, &stm);
    else
        localtime_r (&entetefic.deb_cycle, &stm);

    // "année      mois     jour    heure    minute"
    affiche_ligne ("AN_MOIS_JOUR_H_M");
    printf ("%5d %8d %8d %8d %8d\n", stm.tm_year + 1900, stm.tm_mon + 1,
                                     stm.tm_mday, stm.tm_hour, stm.tm_min);

    // type d'heure utilisée
    if (entetefic.caract_gen & temps_utc)
        // "Cycles synchronisés sur l'heure temps universel"
        affiche_ligne ("CYCLE_SYNC_TU");
    else
        // "Cycles synchronisés sur l'heure locale"
        affiche_ligne ("CYCLE_SYNC_HLOC");

    // type et durée du cycle

    // "\nCycle de mesure "
    affiche_msg ("CYCLE_MESURE");

    if (entetefic.caract_gen & cycle_ireg)
    {
        if (entetefic.caract_gen & cycle_annuel)
            // "annuel"
            affiche_ligne ("ANNUEL");
        else
            // "mensuel"
            affiche_ligne ("MENSUEL");
    }
    else
    {
        // "de"
        affiche_ligne ("CYCLE_DE");

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

        // "jour", "jours"
        // "heure", "heures"
        // "minute", "minutes"
        affnombre (jour, "JOUR", "JOURS");
        affnombre (heure, "HEURE", "HEURES");
        affnombre (minute, "MINUTE", "MINUTES");
    }

    // "%u données par cycle\n"
    printf (message ("NB_DON_CYCLE"), entetefic.nb_don_cycle);
}



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

void affnombre (int valeur, char *unite_singulier, char *unite_pluriel)
{
    if (valeur)
    {
        if (valeur == 1)
            printf ("%d %s\n", valeur, unite_singulier);
        else
            printf ("%d %s\n", valeur, unite_pluriel);
    }
}



// dump de la description de la variable

void dump_entetevar ()
{
    // lecture de la partie fixe de l'entête de la variable du fichier cyloop
    fread (&entetevar, 6, 1, descfic);

    if (entetevar.der_enreg)
        // "Dernière donnée du cycle mise à jour : %u\n"
        printf (message ("DERN_DONNEE_MAJ"), entetevar.der_enreg);
    else
        // "Pas encore de donnée mise à jour dans un cycle"
        affiche_ligne ("AUCUNE_DON_MAJ");

    putchar ('\n');

    // nature des données mémorisées dans la variable

    // "Type de variable : "
    affiche_msg ("TYPE_VARIABLE");

    if (entetevar.typedon & compteur)
    {
        // "compteur de passages sur "
        affiche_msg ("COMPT_PASSAGE_SUR");

        if (entetevar.typedon & donnees_32bits)
            // "32 bits"
            affiche_ligne ("32_BITS");
        else
            // "16 bits"
            affiche_ligne ("16_BITS");
    }
    else
    {
        if (entetevar.typedon & val_float)
            // "valeur en virgule flottante"
            affiche_ligne ("VALEUR_FLOTTANTE");
        else
            // "valeur entière sur 32 bits"
            affiche_ligne ("VAL_ENTIERE_32B");

        if (entetevar.typedon & mem_min_max)
            // "Mémorisation des valeurs min et max à chaque instant du cycle"
            affiche_ligne ("MEMO_MIN_MAX");
    }

    putchar ('\n');

    // calcul de pondération éventuel lors d'un changement de cycle
    if (entetevar.ponderation)
    {
        // "Calcul de pondération lors d'un changement de cycle"
        // "Coefficient de pondération : %4.3f soit %2.1f %% ou %d/256\n"
        affiche_ligne ("CALCUL_PONDER");
        printf (message ("COEF_PONDERATION"), entetevar.ponderation / 256.0,
                entetevar.ponderation / 2.56, entetevar.ponderation);

        if (entetevar.typedon & cpt_cycle_vide)
            // "Calcul de pondération sur les cycles vides"
            affiche_ligne ("PONDER_CYCL_VIDE");
        else
            // "Les cycles vides sont ignorés dans les calculs de pondération"
            affiche_ligne ("NO_POND_CYCL_VID");
    }
    else
        // "Pas de calcul de pondération lors d'un changement de cycle"
        affiche_ligne ("SANS_PONDERATION");

    putchar ('\n');

    // "Mode de calcul de la moyenne des données :"
    affiche_ligne ("MODE_CALC_MOY");

    if (entetevar.typedon & cmoy_sep)
        // "un compteur de passage distinct pour chaque instant du cycle"
        affiche_ligne ("COEF_DISTINCT");
    else
    {
        // "un compteur de passage global pour tout le cycle"
        // "Valeur actuelle du compteur : "
        affiche_ligne ("COEF_GLOBAL");
        affiche_msg ("VAL_COMPTEUR");

        fread (&entetevar.coef_moy, 2, 1, descfic);

        if (entetevar.coef_moy && entetevar.ponderation)
            // "%u/256 soit %4.2f\n"
            printf (message ("VAL_COMPTEUR2"),
                    entetevar.coef_moy, entetevar.coef_moy / 256.0);
        else
            printf ("%u\n", entetevar.coef_moy);
    }

    putchar ('\n');

    if (entetevar.typedon & cumul_donnees)
    {
    // "Plusieurs mise à jour des données possibles à chaque instant d'un cycle"
        affiche_ligne ("MAJ_MULTIPLES");

        if (entetevar.typedon & cmoy_sep)
        {
            // "Le coefficient de calcul de la moyenne est mis à jour "
            affiche_msg ("FREQ_MAJ_MOY");

            if (entetevar.typedon & cpt_toute_don)
                // "à chaque mise à jour de la donnée dans le cycle"
                affiche_ligne ("FREQ_MAJ_MOY1");
            else
                // "une fois pour chaque instant d'un cycle"
                affiche_ligne ("FREQ_MAJ_MOY2");
        }
    }
    else
        // "Une seule mise à jour des données à chaque instant d'un cycle"
        affiche_ligne ("MAJ_UNIQUE_INST");
}



// dump des valeurs mémorisées dans la variable

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

    if (entetevar.typedon & compteur)
    {
        if (entetevar.typedon & donnees_32bits)
            dumpdon_uint32 ();
        else
            dumpdon_uint16 ();
    }
    else
    {
        if (entetevar.typedon & val_float)
            dumpdon_float ();
        else
            dumpdon_int32 ();
    }
}



// dump des données d'une variable compteur sur 16 bits

void dumpdon_uint16 ()
{
    uint16 total;      // la donnée
    uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
    int    instant;      // compteur de boucle


    if (entetevar.typedon & cmoy_sep)
    {
        // titre des colonnes
        // " Instant   Valeur brute       Coef moy      Moyenne          soit"
        affiche_ligne ("INST_BRUT_COEF_M");

        // le dump des données peut commencer
        for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
        {
            // lecture des données d'un instant du cycle
            fread (&coef_moy, 2, 1, descfic);

            if (! fread (&total, 2, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
                err_fatale ("CYLOOP_TRONQUE");

            printf ("%5u %15u %14u", instant, total, coef_moy);

            if (coef_moy)
                printf (" %14f %15f %%\n", (float) total / coef_moy,
                                           100.0 * total / coef_moy);
            else
                puts ("        ------        -------- %");
        }
    }
    else
    {
        // initialisation
        coef_moy = entetevar.coef_moy;

        // titre des colonnes
        // " Instant   Valeur brute      Moyenne          soit"
        affiche_ligne ("INST_VBRUT_MOY");

        // le dump des données peut commencer
        for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
        {
            // lecture des données d'un instant du cycle
            if (! fread (&total, 2, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
                err_fatale ("CYLOOP_TRONQUE");

            printf ("%5u %15u", instant, total);

            if (coef_moy)
                printf (" %14f %14f %%\n", (float) total / coef_moy,
                                           100.0 * total / coef_moy);
            else
                puts ("       --------       -------- %");
        }
    }
}



// dump des données d'une variable compteur sur 32 bits

void dumpdon_uint32 ()
{
    uint32 total;      // la donnée
    uint16 coef_moy; // coefficient diviseur pour le calcul de moyenne
    int    instant;      // compteur de boucle


    if (entetevar.typedon & cmoy_sep)
    {
        // titre des colonnes
        // " Instant   Valeur brute       Coef moy      Moyenne          soit"
        affiche_ligne ("INST_BRUT_COEF_M");

        // le dump des données peut commencer
        for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
        {
            // lecture des données d'un instant du cycle
            fread (&coef_moy, 2, 1, descfic);

            if (! fread (&total, 4, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
                err_fatale ("CYLOOP_TRONQUE");

            printf ("%5u %15u %14u", instant, total, coef_moy);

            if (coef_moy)
                printf (" %14f %15f %%\n", (float) total / coef_moy,
                                           100.0 * total / coef_moy);
            else
                puts ("        ------        -------- %");
        }
    }
    else
    {
        // initialisation
        coef_moy = entetevar.coef_moy;

        // titre des colonnes
        // " Instant   Valeur brute      Moyenne          soit"
        affiche_ligne ("INST_VBRUT_MOY");

        // le dump des données peut commencer
        for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
        {
            // lecture des données d'un instant du cycle
            if (! fread (&total, 4, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
                err_fatale ("CYLOOP_TRONQUE");

            printf ("%5u %15u", instant, total);

            if (coef_moy)
                printf (" %14f %14f %%\n", (float) total / coef_moy,
                                           100.0 * total / coef_moy);
            else
                puts ("       --------       -------- %");
        }
    }
}



// dump des données d'une variable entière sur 32 bits

void dumpdon_int32 ()
{
    // 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 tirées du fichier
    int  instant;     // compteur de boucle


    // calculer debdon et nb_octets

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

        // prise en compte de la valeur coef_moy globale à la variable
        donnees.coef_moy = entetevar.coef_moy;
    }

    if (entetevar.typedon & mem_min_max)
        nb_octets = nb_octets + 8;

    // titre des colonnes
    // " Instant   Valeur brute   "
    affiche_msg ("INST_VBRUTE");

    if (entetevar.typedon & cmoy_sep)
        // " Coef moy     "
        affiche_msg ("COEF_MOY");

    if (entetevar.typedon & mem_min_max)
        // "   Minimum       Moyenne       Maximum"
        affiche_ligne ("MIN_MOY_MAX");
    else
        // "   Moyenne"
        affiche_ligne ("MOYENNE");

    // le dump des données peut commencer
    for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
    {
        // lecture des données d'un instant du cycle
        if (! fread (debdon, nb_octets, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
            err_fatale ("CYLOOP_TRONQUE");

        printf ("%5u %15d", instant, donnees.total);

        if (entetevar.typedon & cmoy_sep)
            printf (" %13u", donnees.coef_moy);

        if (entetevar.typedon & mem_min_max)
        {
            if (donnees.coef_moy)
                printf (" %12d %16.3f %14d", donnees.min,
                       (float) donnees.total / donnees.coef_moy, donnees.max);
            else
                printf (" %14d         ----- %13d", donnees.min, donnees.max);
        }
        else
        {
            if (donnees.coef_moy)
                printf (" %15f", (float) donnees.total / donnees.coef_moy);
            else
                printf ("          -----");
        }

        putchar ('\n');
    }
}



// dump des données d'une variable en virgule flottante

void dumpdon_float ()
{
    // la structure de donnée maximale pour une variable en virgule flottante
    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 tirées du fichier
    int  instant;     // compteur de boucle
    int  multip;    // pour tenir compte du rapport de 256 éventuel
                    // entre coef_cmul et les valeurs


    // calculer debdon et nb_octets

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

        // prise en compte de la valeur coef_moy globale à la variable
        donnees.coef_moy = entetevar.coef_moy;
    }

    if (entetevar.typedon & mem_min_max)
        nb_octets = nb_octets + 8;

    // rapport entre coef_moy et les valeurs
    if (entetevar.ponderation)
        multip = 256;
    else
        multip = 1;

    // titre des colonnes
    // " Instant   Valeur brute   "
    affiche_msg ("INST_VBRUTE");

    if (entetevar.typedon & cmoy_sep)
        // " Coef moy     "
        affiche_msg ("COEF_MOY");

    if (entetevar.typedon & mem_min_max)
        // "   Minimum       Moyenne       Maximum"
        affiche_ligne ("MIN_MOY_MAX");
    else
        // "   Moyenne"
        affiche_ligne ("MOYENNE");

    // le dump des données peut commencer
    for (instant = 1; instant <= entetefic.nb_don_cycle; instant++)
    {
        // lecture des données d'un instant du cycle
        if (! fread (debdon, nb_octets, 1, descfic))
         // "Erreur de lecture du fichier cyloop : fichier probablement tronqué"
            err_fatale ("CYLOOP_TRONQUE");

        printf ("%5u %15f", instant, donnees.total);

        if (entetevar.typedon & cmoy_sep)
            printf (" %13u", donnees.coef_moy);

        if (entetevar.typedon & mem_min_max)
        {
            if (donnees.coef_moy)
                printf (" %16e %15e %15e", donnees.min,
                      donnees.total / donnees.coef_moy * multip, donnees.max);
            else
                printf (" %16e       ----- %15e", donnees.min, donnees.max);
        }
        else
        {
            if (donnees.coef_moy)
                printf (" %17e", donnees.total / donnees.coef_moy * multip);
            else
                printf ("          -----");
        }

        putchar ('\n');
    }
}