/*
** Calcul le nombre d'unités entre 2 heures
**
** USAGE : UCalc JOUR HEURE_DEBUT HEURE_FIN "VILLE"
** lundi 5-Avril-93
*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <exec/types.h>


#define VERSION "0.9"
#define DATE "5.4.93"

/* Longueur maximales des différents types de chaines */
#define MAXJOUR 8
#define MAXVILLE 40
#define MAXLINE 256

/* Styles Ansi */
#define ANSI_BOLD       "›1m"
#define ANSI_WHITE      "›2m"                           /* more or less... */
#define ANSI_NORMAL     "›0m"

/* Chaine de version pour la commande Version */
static char AmVersion[] = "$VER: UCalc v" VERSION " (" DATE ")";

/* structure de description */
static struct Comm
{
        /* heure logon */
        LONG time1;
        /* heure logoff */
        LONG time2;
        /* jour */
        char jour[MAXJOUR + 1];
        /* ville */
        char ville[MAXVILLE + 1];
};

/* handlers des fichiers de config */
static FILE *codes = NULL;
static FILE *distances = NULL;
static FILE *horaires = NULL;
static FILE *tarifs = NULL;

/* structures utile à l'ouverture des fichiers de config */
static struct ConfigFile
{
        char *name;
        FILE **handler;
};

/* on peut ici modifier le nom des fichiers de config */
static struct ConfigFile CFiles[] =
{
        {"Codes", &codes},
        {"Distances", &distances},
        {"Horaires", &horaires},
        {"Tarifs", &tarifs},
        {NULL, NULL}
};

/* Prototypage des fonctions */
BOOL ParseArgs(struct Comm *comm, int ac, char *av[]);
LONG atotime(const char *timestr);
FILE *OpenConfigFile(const char *filename);
BOOL OpenAllFiles(struct ConfigFile *cfile);
void CloseAllFiles(struct ConfigFile *cfile);
WORD CalcUnits(struct Comm *comm);
BOOL GetDayCode(char *jour, WORD * code, FILE * file);
WORD GetNextDayCode(FILE * file);
WORD GetDistance(char *ville, WORD * dist, FILE * file);
WORD GetUnitPlage(WORD jour, WORD dist, LONG time1, LONG time2);
WORD CalcUnitPlage(WORD dist, char *plage, LONG time, FILE * file);
BOOL FileScan(FILE * file, char *buffer);


/************************
** Ensemble des fonctions
*************************/

/*
** main
*/
int
main(int argc, char *argv[])
{
        struct Comm comm;
        WORD units;

        if (!ParseArgs(&comm, argc, argv))
                exit (EXIT_FAILURE);

        if (OpenAllFiles(CFiles))
                if (units = CalcUnits(&comm))
                      printf("%d\n", units);

        CloseAllFiles(CFiles);
        return (EXIT_SUCCESS);
}


/*
** ParseArgs
** Recupère les données passées en argument
*/
BOOL
ParseArgs(struct Comm * comm, int ac, char *av[])
{
        if (ac != 5)
        {
                if (*av[1] == '?' && ac == 2)
                        printf( ANSI_BOLD ANSI_WHITE
                                                "%s V" VERSION " par Christophe Guillon & Pascal Rullier\n© 1993 BUGSS"
                                                ANSI_NORMAL "\n", av[0] );
                printf( "Usage : %s JOUR HEURE1 HEURE2 VILLE\n"
                                                "        %s ?\n", av[0], av[0]);
                return FALSE;
        }
        strncpy(comm->jour, av[1], MAXJOUR);
        comm->jour[MAXJOUR] = '\0';
        strncpy(comm->ville, av[4], MAXVILLE);
        comm->ville[MAXVILLE] = '\0';

        if ((comm->time1 = atotime(av[2])) == -1)
        {
                printf("Heure de début incorrecte\n");
                return FALSE;
        }
        if ((comm->time2 = atotime(av[3])) == -1)
        {
                printf("Heure de fin incorrecte\n");
                return FALSE;
        }
        return TRUE;
}


/*
** atotime
** Convertie une chaine HH:MM:SS en nombre de secondes depuis 00:00:00
** renvoie -1 si chaine invalide
*/
LONG
atotime(const char *timestr)
{
        UWORD h, m, s;
        if (sscanf(timestr, "%hd:%hd:%hd", &h, &m, &s) != 3 ||
                h > 23 ||
                m > 59 ||
                s > 59)
                return -1L;
        return (3600 * h + 60 * m + s);
}

/*
** OpenConfigFile
** ouvre le fichier spécifié en lecture
** recherche dans le répertoire courant puis dans s: puis env:
** retourne le FILE *
*/
FILE *
OpenConfigFile(const char *filename)
{
        FILE *file;

        /* on essaye le répertoire courant */
        if (!(file = fopen(filename, "r")))
        {
                char buffer[40];
                /* on essaye dans S: */
                strcpy(buffer, "S:");
                strcat(buffer, filename);
                if (!(file = fopen(buffer, "r")))
                {
                        /* on essaye dans ENV: */
                        strcpy(buffer, "ENV:");
                        strcat(buffer, filename);
                        file = fopen(buffer, "r");
                }
        }
        return file;
}

/*
** OpenAllFiles
** ouvre tous les fichiers de config spécifié dans le tableau
*/
BOOL
OpenAllFiles(struct ConfigFile * cfile)
{
        BOOL ok = TRUE;

        while (cfile->name)
        {
                if (!(*cfile->handler = OpenConfigFile(cfile->name)))
                {
                        printf("Fichier de configuration %s introuvable\n", cfile->name);
                        ok = FALSE;
                        break;
                }
                cfile++;
        }
        return ok;
}

/*
** CloseAllFiles
** ferme tous les fichiers de config ouverts
*/
void
CloseAllFiles(struct ConfigFile *cfile)
{
        /* on parcours jusqu'à ce qu'un handler pointe sur NULL
                 tous les autres fichiers n'ont pas été ouverts */
        while (*cfile->handler)
        {
                fclose(*cfile->handler);
                cfile++;
        }
}

/*
** FileScan
** Récupère une ligne d'un fichier en sautant les commentaires
** et les lignes blanches
*/
BOOL
FileScan(FILE *file, char *buffer)
{
        do
                if (!fgets(buffer, MAXLINE, file))
                        return FALSE;
        while ( *buffer == '#' || strspn(buffer, " \t\n") == strlen(buffer) ) ;
        return TRUE;
}

/*
** CalcUnits
** calcule le nombre d'unités téléphoniques
*/
WORD
CalcUnits(struct Comm * comm)
{
        WORD daycode;
        WORD distance;
        WORD nbunits = 0;
        LONG t1 = comm->time1;
        LONG t2 = comm->time2;

        if (!(GetDayCode(comm->jour, &daycode, codes) &&
                        GetDistance(comm->ville, &distance, distances)))
                return 0;

        if (t2 < t1)
  {
        nbunits += GetUnitPlage(daycode, distance, t1, 23*3600+59*60+56);
        nbunits += GetUnitPlage(GetNextDayCode(codes), distance, 0L, t2);
        }
        else
                nbunits = GetUnitPlage(daycode, distance, t1, t2);

        return nbunits;
}

/*
** GetDayCode
** Récupère le code du jour
*/
BOOL
GetDayCode(char *jour, WORD * code, FILE * file)
{
        char buffer[MAXLINE];
        BOOL ok;

        do
                ok = FileScan(file, buffer);
        while (ok && stricmp(strtok(buffer, " \t\n"), jour));

        if (!ok)
        {
                printf("Jour incorrect\n");
                return FALSE;
        }
        *code = atoi(strtok(NULL, " \t\n"));
        return TRUE;
}

/*
** GetNextDayCode
** Récupère le code du jour suivant
** a appeler seulement si GetDayCode a renvoyé un jour
*/
WORD
GetNextDayCode(FILE * file)
{
        char buffer[MAXLINE];

        if(!FileScan(file, buffer))
        {
                rewind(file);
                FileScan(file, buffer);
  }
        strtok(buffer, " \t\n");
        return (WORD)atoi(strtok(NULL, " \t\n"));
}

/*
** GetDistance
** Récupère la distance de la ville
*/
WORD
GetDistance(char *ville, WORD * dist, FILE * file)
{
        char buffer[MAXLINE];
        BOOL ok;
        int len = strlen(ville);

        do
                ok = FileScan(file, buffer);
        while (ok && strnicmp(buffer, ville, len));

        if (!ok)
        {
                printf("Ville incorrecte\n");
                return FALSE;
        }
        *dist = atoi(&buffer[len]);
        return TRUE;
}

/*
** GetUnitPlage
** Récupère la plage horaire et ranvoie le nombre d'unités écoulées
** modifie aussi l'heure de debut
*/
WORD
GetUnitPlage(WORD jour, WORD dist, LONG time1, LONG time2)
{
        char buffer[MAXLINE];
        BOOL ok;
        LONG p1, p2;
        WORD nbunits = 0;

        do
        {
                do
                        ok = FileScan(horaires, buffer);
                while (ok && !(atoi(strtok(buffer, " \t\n")) == jour &&
                                time1 >= (p1 = atotime(strtok(NULL, " \t\n"))) &&
                                time1 <= (p2 = atotime(strtok(NULL, " \t\n")))));
                rewind(horaires);

                p1 = time1;
                if (time2 <= p2)
                {
                        p2 = time2;
                        time1 = time2 + 1;
                }
                else
                        time1 = p2 + 1;
                nbunits += CalcUnitPlage(dist, strtok(NULL, " \t\n"), p2 - p1, tarifs);
  }
        while (time1 <= time2);

        return nbunits;
}

/*
** CalcUnitPlage
** calcul le nombre d'unités dans un plage de temps donnée
** etant donnée la distance et le nom de la plage horaire
**
*/
WORD
CalcUnitPlage(WORD dist, char *plage, LONG time, FILE * file)
{
        char buffer[MAXLINE];
        BOOL ok;
        WORD utime;

        do
                ok = FileScan(file, buffer);
        while (ok && !(atoi(strtok(buffer, " \t\n")) == dist &&
                        stricmp(strtok(NULL, " \t\n"), plage) == 0));

        rewind(file);

        if (!ok)
                return 0;

        utime = atoi(strtok(NULL, " \t\n"));

        return (WORD)(time / utime + 1);

}
