/*
    Fichier cylincr.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 d'incrémenter la variable d'un fichier
    cyloop si elle est de type compteur.
*/


#include <stdio.h>
#include <time.h>
#include "types.h"
#include "limites.h"
#include "messages.h"
#include "majcycle.h"


// prototypes
void pondere (int coef_pond);
void incrval (uint16 num_instant, int majcmoy);



// 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
    struct tm stm;           // structure contenant la date et l'heure courante
    uint32    timep;         // date et heure dans un uint32
    int       maj_entetefic; // indicateur pour mise à jour fichier
    uint16    num_instant;     // numéro de la donnée à incrémenter
    uint32    duree_cycle;   // durée du cycle


    // 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 (varg [1]);
    else
    {
        // "Nom de fichier manquant"
        // "Syntaxe : %s nom_fichier_cyloop"
        affiche_err ("MANQUE_NOMFICH");
        psyntaxe ("SYNT_CYLINCR");
    }

    // ouverture en lecture / écriture du fichier cyloop
    descfic = fopen (nomfic, "r+");

    if (! descfic)
        // "Fichier_cyloop %s inexistant ou protégé en lecture ou écriture"
        err_fatale_arg ("ACCES_CYLOOP_RW",
                                                                   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;
    }

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

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

    // vérification de la nature de la variable
    if (! (entetevar.typedon & compteur))
    {
   // "La variable du fichier cyloop n'est pas un compteur : utiliser cyladdval"
        affiche_err ("VAR_NON_COMPTEUR");

        fclose (descfic);
        return;
    }

    // fin de lecture de l'entête de la variable si nécessaire
    if (! (entetevar.typedon & cmoy_sep))
        fread (&entetevar.coef_moy, 2, 1, descfic);

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

    // si le début du cycle est ultérieur à l'instant présent
    if (timep < entetefic.deb_cycle)
    {
        fclose (descfic);

        // "Régression dans le temps : une intervention manuelle "
        // "sur le fichier cyloop ou l'horloge est nécessaire"
        affiche_err ("REGRES_TEMPS");
        err_fatale ("REGRES_TEMPS2");
    }

    // initialisation indicateurs de mise à jour du fichier
    maj_entetefic = NON;

    // si on utilise l'heure locale
    if (! (entetefic.caract_gen & temps_utc))
    {
        // récupérer le détail de la date et de l'heure locale
        localtime_r (&timep, &stm);

        // si changement d'heure été/hiver
        if ((entetefic.etat_cyloop & heure_ete) != stm.tm_isdst)
            // prendre en compte ce changement
            maj_entetefic = maj_type_heure (stm.tm_isdst, timep);
    }

    // si changement de cycle
    if (entetefic.deb_cycle + entetefic.duree_cycle <= timep)
    {
        // prise en compte de ce changement de cycle
        changement_cycle (timep);

        // une manière de mémoriser un début de cycle
        entetevar.der_enreg = 0;

        // on reportera la mise à jour dans le fichier cyloop
        maj_entetefic = OUI;
    }

    // si mise à jour de l'entête du fichier cyloop nécessaire
    if (maj_entetefic)
    {
        // recopier la nouvelle entête
        rewind (descfic);
        fwrite (&entetefic, 16, 1, descfic);
    }

    // récupération de la durée du cycle actuel
    duree_cycle = duree_cycle_actuel (&timep);

    // calcul de l'instant du cycle à mettre à jour
    num_instant = calcul_instant (timep, duree_cycle);

    // si on a changé de cycle et une seule valeur coef_moy pour la variable
    if (! entetevar.der_enreg && !(entetevar.typedon & cmoy_sep))
    {
        // mettre à jour cette valeur
        if (entetevar.ponderation)
            entetevar.coef_moy += 256;
        else
            entetevar.coef_moy ++;

        // y compris dans le fichier cyloop
        fseek (descfic, 22, SEEK_SET);
        fwrite (&entetevar.coef_moy, 2, 1, descfic);
    }

    // si c'est la première mise à jour effectuée durant ce cycle
    // ou si on passe à un autre instant du même cycle
    if (entetevar.der_enreg < num_instant)
    {
        // mettre à jour le numéro de l'instant du cycle traité
        entetevar.der_enreg = num_instant;

        // y compris dans le fichier cyloop
        fseek (descfic, 16, SEEK_SET);
        fwrite (&entetevar.der_enreg, 2, 1, descfic);

        // incrémenter le compteur et si nécessaire le
        // champ coef_moy pour l'instant du cycle concerné
        incrval (num_instant, OUI);
    }
    // sinon, si on a déjà mis à jour le compteur pour cet instant du cycle
    else if (entetevar.der_enreg == num_instant)
    {
        // si on est autorisé à le mettre à jour plusieur
        // fois pour chacun des instants du cycle
        if (entetevar.typedon & cumul_donnees)
            // incrémenter le compteur et si nécessaire le
            // champ coef_moy pour l'instant du cycle concerné
            incrval (num_instant, entetevar.typedon & cpt_toute_don);
    }
    // sinon, il y a régression dans le temps
    else
    {
        // vérifier si elle est supérieure à 2 heures
        if (calcul_instant (timep + 7200, duree_cycle) < entetevar.der_enreg)
        {
            // vérifier si elle est supérieure à 1 journée
            // et afficher le message d'erreur approprié
            if (calcul_instant (timep + secjour, duree_cycle) <
                                                 entetevar.der_enreg)
            {
                // "Forte régression dans le temps : une intervention manuelle "
                // "sur le fichier cyloop ou l'horloge est nécessaire"
                affiche_err ("FORTE_REGR_TEMPS");
                affiche_err ("REGRES_TEMPS2");
            }
            else
              // "Régression dans le temps : vérifier l'horloge de l'ordinateur"
                affiche_err ("REGR_VERIF_HORL");
        }
    }

    // le fichier cyloop a été mis à jour
    fclose (descfic);
}



// calcul de pondération sur les cycles antérieurs

void pondere (int coef_pond)
{
    uint16 coef_moy; // coef_moy si distinct pour chaque instant du cycle
    uint16 valeur16b;  // valeur à pondérer si elle est sur 16 bits
    uint32 valeur32b;  // valeur à pondérer si elle est sur 32 bits
    uint32 valmaj;     // valeur sur 32 bits pour calcul de pondération
    int    i;          // compteur


    // si coefficient coef_moy global au compteur
    if (! (entetevar.typedon & cmoy_sep))
    {
        // mettre à jour ce coefficient
        // le calcul de multiplication et division est fait sur 32 bits
        valmaj = entetevar.coef_moy;
        entetevar.coef_moy = (valmaj * coef_pond) >> 8;

        // ce coefficient sera mis à jour plus tard dans le fichier

        // se positionner sur la première valeur du compteur
        fseek (descfic, 24, SEEK_SET);

        // si compteur sur 32 bits
        if (entetevar.typedon & donnees_32bits)
        {
            // mise à jour des valeurs du compteur 32 bits
            for (i = entetefic.nb_don_cycle; i > 0; i--)
            {
                // resynchronisation position dans le fichier
                // fseek (descfic, 0, SEEK_CUR); (vérifier utilité)

                // récupération de la valeur du compteur sur 32 bits
                fread (&valeur32b, 4, 1, descfic);

                // si risque de débordement lors du calcul
                if (valeur32b & 0xFF000000)
                    // faire la division d'abord
                    valeur32b = (valeur32b >> 8) * coef_pond;

                // sinon faire la multiplication d'abord
                else
                    valeur32b = (valeur32b * coef_pond) >> 8;

                // mettre à jour la valeur du compteur dans le fichier
                fseek (descfic, -4, SEEK_CUR);
                fwrite (&valeur32b, 4, 1, descfic);
            }
        }
        // sinon (compteur sur 16 bits)
        else
        {
            // mise à jour des valeurs du compteur 16 bits
            for (i = entetefic.nb_don_cycle; i > 0; i--)
            {
                // resynchronisation position dans le fichier
                // fseek (descfic, 0, SEEK_CUR); (vérifier utilité)

                // récupération de la valeur du compteur sur 16 bits
                fread (&valeur16b, 2, 1, descfic);

                // on fait le calcul de pondération sur 32 bits
                valmaj = valeur16b;
                valeur16b = (valmaj * coef_pond) >> 8;

                // mettre à jour la valeur du compteur dans le fichier
                fseek (descfic, -2, SEEK_CUR);
                fwrite (&valeur16b, 2, 1, descfic);
            }
        }
    }
    // sinon
    else
    {
        // se positionner sur le premier coefficient coef_moy de la variable
        fseek (descfic, 22, SEEK_SET);

        // si compteur sur 32 bits
        if (entetevar.typedon & donnees_32bits)
        {
            // mise à jour des valeurs de coef_moy et du compteur 32 bits
            for (i = entetefic.nb_don_cycle; i > 0; i--)
            {
                // resynchronisation position dans le fichier
                // fseek (descfic, 0, SEEK_CUR); (vérifier utilité)

                // récupération de la valeur du coef_moy
                fread (&coef_moy, 2, 1, descfic);

                // récupération de la valeur du compteur sur 32 bits
                fread (&valeur32b, 4, 1, descfic);

                // calcul de pondération pour coef_moy
                // le calcul de multiplication et division est fait sur 32 bits
                valmaj = coef_moy;
                coef_moy = (valmaj * coef_pond) >> 8;

                // calcul de pondération pour la valeur du compteur
                // si risque de débordement lors du calcul
                if (valeur32b & 0xFF000000)
                    // faire la division d'abord
                    valeur32b = (valeur32b >> 8) * coef_pond;

                // sinon faire la multiplication d'abord
                else
                    valeur32b = (valeur32b * coef_pond) >> 8;

                // mettre à jour les valeurs dans le fichier
                fseek (descfic, -6, SEEK_CUR);
                fwrite (&coef_moy, 2, 1, descfic);
                fwrite (&valeur32b, 4, 1, descfic);
            }
        }
        // sinon (compteur sur 16 bits)
        else
        {
            // mise à jour des valeurs de coef_moy et du compteur 16 bits
            // la même boucle sert aux 2 valeurs, moins optimisé pour les
            // accès au fichier, mais plus simple
            for (i = entetefic.nb_don_cycle * 2; i > 0; i--)
            {
                // resynchronisation position dans le fichier
                // fseek (descfic, 0, SEEK_CUR); (vérifier utilité)

                // récupération de la valeur du compteur sur 16 bits
                fread (&valeur16b, 2, 1, descfic);

                // on fait le calcul de pondération sur 32 bits
                valmaj = valeur16b;
                valeur16b = (valmaj * coef_pond) >> 8;

                // mettre à jour la valeur du compteur dans le fichier
                fseek (descfic, -2, SEEK_CUR);
                fwrite (&valeur16b, 2, 1, descfic);
            }
        }
    }
}



// incrémente le compteur pour l'instant num_instant du cycle

void incrval (uint16 num_instant, int majcmoy)
{
    int    ajout;      // valeur à rajouter au compteur
    long   posval;     // position de la valeur à incrémenter dans le fichier
    uint16 coef_moy; // coefficient coef_moy pour la valeur à incrémenter
    uint16 valeur16b;  // valeur à incrémenter si elle est sur 16 bits
    uint32 valeur32b;  // valeur à incrémenter si elle est sur 32 bits
    int    majfich;    // indique si on devra mettre à jour le fichier


    // initialisation
    majfich = OUI;

    // calcul de la valeur à rajouter au compteur
    if (entetevar.ponderation)
        ajout = 256;
    else
        ajout = 1;

    // pour le calcul de position, le premier instant
    // du cycle n'entrainera pas de décalage
    num_instant --;

    // calcul de la position dans le fichier de la valeur à incrémenter
    posval = 24;

    if (entetevar.typedon & cmoy_sep)
        posval += num_instant * 2;

    if (entetevar.typedon & donnees_32bits)
        posval += num_instant * 4;
    else
        posval += num_instant * 2;

    // si coefficient coef_moy à mettre à jour
    if (majcmoy && (entetevar.typedon & cmoy_sep))
    {
        // se positionner dans le fichier sur la zone à modifier
        fseek (descfic, posval - 2, SEEK_SET);

        // récupérer et incrémenter le coefficient coef_moy
        fread (&coef_moy, 2, 1, descfic);
        coef_moy += ajout;
    }
    // sinon, juste valeur du compteur à modifier
    else
        // se positionner dans le fichier sur la zone à modifier
        fseek (descfic, posval, SEEK_SET);

    // si le compteur est sur 32 bits
    if (entetevar.typedon & donnees_32bits)
    {
        // récupération de la valeur du compteur sur 32 bits
        fread (&valeur32b, 4, 1, descfic);

        // incrementation de la valeur du compteur
        valeur32b += ajout;
    }
    // sinon (compteur sur 16 bits)
    else
    {
        // récupération de la valeur du compteur sur 16 bits
        fread (&valeur16b, 2, 1, descfic);

        // s'il n'y a pas encore eu de débordement numérique
        if (valeur16b != max_uint16)
        {
            // incrémenter la valeur (calcul sur 32 bits)
            valeur32b = (uint32) valeur16b + ajout;

            // si débordement numérique
            if (valeur32b & 0xFFFF0000)
            {
                // message d'erreur
                // "Débordement numérique pour la variable"
                // "Il faudrait la convertir en 32 bits"
                affiche_err ("DEBORD_NUMERIQUE");
                affiche_err ("BESOIN_CONV32B");

                // le compteur prendra la valeur maximale
                valeur16b = max_uint16;
            }
            // sinon, mémorisation de la nouvelle valeur du compteur
            else
                valeur16b = valeur32b;
        }
        // sinon, message de rappel débordement numérique
        else
        {
            // "Rappel : Débordement numérique pour la variable"
            // "Il faudrait la convertir en 32 bits"
            affiche_err ("PAR_DEBORD_NUM");
            affiche_err ("BESOIN_CONV32B");

            // pas la peine de mettre à jour le fichier pour la variable
            majfich = NON;
        }
    }

    // si coefficient coef_moy à mettre à jour
    if (majcmoy && (entetevar.typedon & cmoy_sep))
    {
        // se positionner dans le fichier sur la zone à modifier
        fseek (descfic, posval - 2, SEEK_SET);

        // mettre à jour le coefficient coef_moy
        fwrite (&coef_moy, 2, 1, descfic);
    }
    // sinon si juste mise à jour de la variable
    else if (majfich)
    {
        // se positionner dans le fichier sur la zone à modifier
        fseek (descfic, posval, SEEK_SET);
    }

    // mettre à jour la valeur de la variable (en fonction de sa taille)
    if (majfich)
    {
        if (entetevar.typedon & donnees_32bits)
            fwrite (&valeur32b, 4, 1, descfic);
        else
            fwrite (&valeur16b, 2, 1, descfic);
    }
}