/*
    Fichier majcycle.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


    Bibliothèque de fonctions permettant de prendre en compte
    un changement de cycle ou de se positionner dans le cycle
    en cours pour un mise à jour du fichier cyloop.
*/


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


// variables globales (pour éviter des tas de passages de paramètre)
extern FILE        *descfic;  // descripteur du fichier cyloop
extern desc_cyloop entetefic; // entête du fichier cyloop
extern desc_var    entetevar; // entête de la variable du fichier 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");
        }
    }

    // retourner le nom du fichier
    return nomfic;
}



/*
    Prend en compte un passage à l'heure d'hiver ou d'été

    retourne le valeur OUI ou NON selon qu'il faille reporter
    une mise à jour d'entetefic dans le fichier cyloop
*/


int maj_type_heure (int passage_ete, long timep)
{
    // si on vient juste de passer à l'heure d'été
    if (passage_ete)
    {
        // mémoriser le changement d'heure
        entetefic.etat_cyloop = entetefic.etat_cyloop | heure_ete;

        // et modifier l'instant de début du cycle
        entetefic.deb_cycle = entetefic.deb_cycle - 3600;

        // on reportera plus tard la mise à jour dans le fichier cyloop
        return (OUI);
    }
    // sinon on vient juste de passer à l'heure d'hiver
    else
    {
        // si ce changement d'heure ne fait pas revenir au cycle précédent
        if (entetefic.deb_cycle + 3600 <= timep)
        {
            // mémoriser le changement d'heure
            entetefic.etat_cyloop = entetefic.etat_cyloop & mask_heure_hiver;

            // et modifier l'instant de début du cycle
            entetefic.deb_cycle = entetefic.deb_cycle + 3600;

            // on reportera plus tard la mise à jour dans le fichier cyloop
            return (OUI);
        }
        // sinon
        else
            // le changement d'heure sera pris en compte une autre fois
            return (NON);
    }
}



// prise en compte d'un changement de cycle

void changement_cycle (long timep)
{
    // structure contenant la date et l'heure du début du cycle
    struct tm oldstm;
    int    cycles_sautes; // compteur de cycles
    uint32 duree_cycle;   // durée du cycle
    int    coef_pond;     // coefficient de pondération à appliquer


    // initialisation, durée du cycle
    duree_cycle = entetefic.duree_cycle;

    // si cycle de longueur irrégulière basé sur le calendrier
    if (entetefic.caract_gen & cycle_ireg)
    {
        // découper les caractéristique de l'instant du début de cycle
        if (entetefic.caract_gen & temps_utc)
            gmtime_r (&entetefic.deb_cycle, &oldstm);
        else
            localtime_r (&entetefic.deb_cycle, &oldstm);

        // initialisation compteur
        cycles_sautes = 0;

        // si cycle annuel
        if (entetefic.caract_gen & cycle_annuel)
        {
            // on avance année par année
            do
            {
                entetefic.deb_cycle += duree_cycle;

                if (++ oldstm.tm_year % 4)
                    duree_cycle = 365 * secjour;
                else
                    duree_cycle = 366 * secjour;

                cycles_sautes ++;
            }
            // jusqu'à ce qu'on tombe sur la bonne
            while (entetefic.deb_cycle + duree_cycle <= timep);
        }
        // sinon, cycle mensuel
        else
        {
            // on avance mois par mois
            do
            {
                entetefic.deb_cycle += duree_cycle;

                switch (++ oldstm.tm_mon)
                {
                             // février
                    case  1: if (oldstm.tm_year % 4)
                                 entetefic.duree_cycle = 28 * secjour;
                             else
                                 entetefic.duree_cycle = 29 * secjour;

                             break;

                    case  3: // mois de 30 jours
                    case  5:
                    case  8:
                    case 10: entetefic.duree_cycle = 30 * secjour;
                             break;

                             // janvier, changement d'année
                    case 12: oldstm.tm_mon = 0;
                             oldstm.tm_year ++;

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

                cycles_sautes ++;
            }
            // jusqu'à ce qu'on tombe sur le bon
            while (entetefic.deb_cycle + duree_cycle <= timep);
        }
    }
    // sinon (cas général, cycle de longueur régulière)
    else
    {
        // calculer de combien de cycles on avance
        cycles_sautes = (timep - entetefic.deb_cycle) / duree_cycle;

        // calculer l'instant de début du cycle courant
        entetefic.deb_cycle += cycles_sautes * duree_cycle;
    }

    // si calcul de pondération
    if (entetevar.ponderation)
    {
        // calcul du coefficient de pondération
        coef_pond = entetevar.ponderation;

        if (entetevar.typedon & cpt_cycle_vide)
        {
            while (--cycles_sautes)
                coef_pond = (coef_pond * entetevar.ponderation) >> 8;
        }

        // appliquer le calcul de pondération sur la variable
        pondere (coef_pond);
    }
}



// Retourne la durée du cycle actuel en secondes
// saute si nécessaire le 29 février dans les cycles annuels

uint32 duree_cycle_actuel (int32 *timep)
{
    struct tm stm;  // structure contenant la date et l'heure courante


    // si cycle calendaire de longueur irrégulière
    if (entetefic.caract_gen & cycle_ireg)
    {
        // dans le cas des cycles annuels
        if (entetefic.caract_gen & cycle_annuel)
        {
            // récupérer la date courante
            if (entetefic.caract_gen & temps_utc)
                gmtime_r (timep, &stm);
            else
                localtime_r (timep, &stm);

            // sauter le 29 février le cas échéant
            if ((stm.tm_mon >= 2) && (stm.tm_year % 4))
                *timep = *timep + secjour;

            // l'année est compté sur 366 jours pour le graphique
            return (366 * secjour);
        }
        // sinon, on est dans le cas des cycles mensuels
        else
            // le mois est compté sur 31 jours pour le graphique
            return (31 * secjour);
    }
    // sinon (cas général, cycle de longueur régulière)
    else
        // on utilise la vraie durée du cycle
        return (entetefic.duree_cycle);
}



// calcul du numéro de l'instant du cycle à mettre à jour

uint16 calcul_instant (uint32 timep, uint32 duree_cycle)
{
    uint32 depuis_deb_cycle; // temps écoulé depuis le début du cycle
    uint16 result;           // résultat du calcul


    // temps écoulé depuis le début du cycle courant
    depuis_deb_cycle = timep - entetefic.deb_cycle;

    // si on peut calculer le numéro d'instant
    // sans débordement mathématique
    if (((depuis_deb_cycle * entetefic.nb_don_cycle) / entetefic.nb_don_cycle)
                                                          == depuis_deb_cycle)
        // calcul exact en arithmétique entière
        result = depuis_deb_cycle * entetefic.nb_don_cycle / duree_cycle;
    else
        // sinon calcul approché en arithmétique flottante
        result = (float)depuis_deb_cycle * entetefic.nb_don_cycle / duree_cycle;

    // retour du résultat, les numéros d'instant
    // vont de 1 à entetefic.nb_don_cycle
    return (result + 1);
}