/*
This source file is part of Scol
For the latest info, see http://www.scolring.org

Copyright (c) 2010 Stephane Bisaro, aka Iri <iri@irizone.net>

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA, or go to
http://www.gnu.org/copyleft/lesser.txt

For others informations, please contact us from http://www.scolring.org/
*/


#include "main.h"


/* function from http://nicolasj.developpez.com/articles/libc/string/ */ /* not used */
char *str_sub (const char *s, unsigned int start, unsigned int end)
{
   char *new_s = NULL;

   if (s != NULL && start < end)
   {
      new_s = malloc (sizeof (*new_s) * (end - start + 2));
      if (new_s != NULL)
      {
         unsigned int i;
         for (i = start; i <= end; i++)
         {
            new_s[i-start] = s[i];
         }
         new_s[i-start] = '\0';
      }
      else
      {
         fprintf (stderr, "Memoire insuffisante\n");
         exit (EXIT_FAILURE);
      }
   }
   return new_s;
}








/*
    display decoded metar result

*/
static void displayMetarDecoded (OutputMetar OM)
{
    printf (">>>> %s : %s\n", "TYPE MESSAGE", OM.code);
    printf (">>>> %s : %s\n", "STATION", OM.oaci);
    printf (">>>> %s : %s\n", "DAY", OM.day);
    printf (">>>> %s : %s\n", "HOUR", OM.hour);
    printf (">>>> %s : %s\n", "MINUTE", OM.minute);
    printf (">>>> %s : %s\n", "WIND -> DIR", OM.wind_dir);
    printf (">>>> %s : %s\n", "WIND -> SPEED", OM.wind_speed);
    printf (">>>> %s : %s\n", "WIND -> GUSTING", OM.wind_gust);
    printf (">>>> %s : %s\n", "WIND -> UNIT", OM.wind_unit);
    printf (">>>> %s : %s\n", "WIND -> VAR N", OM.wind_var_n);
    printf (">>>> %s : %s\n", "WIND -> VAR X", OM.wind_var_x);
    printf (">>>> %s : %s\n", "WEATHER", OM.weather);
    printf (">>>> %s : %s\n", "VISIBILITY", OM.visibility);
    printf (">>>> %s : %s\n", "NEBULOSITY LOW", OM.nebul_low);
    printf (">>>> %s : %s\n", "NEBULOSITY MED", OM.nebul_med);
    printf (">>>> %s : %s\n", "NEBULOSITY HIGH", OM.nebul_high);
    printf (">>>> %s : %s\n", "TEMPERATURE MESUREE", OM.temp);
    printf (">>>> %s : %s\n", "TEMPERATURE ROSEE", OM.temp_d);
    printf (">>>> %s : %s\n", "PRESSION", OM.pressure);
    printf (">>>> %s : %s\n", "RECENT WEATHER", OM.reweather);
    printf ("%s", "\n");

    return;
}

/*
    Initialisation et allocation

*/
static void initStructMetar (OutputMetar * ptr)
{
    memset (ptr->code, '\0', 6);
    memset (ptr->oaci, '\0', 5);
    memset (ptr->wind_dir, '\0', 4);
    memset (ptr->wind_speed, '\0', 3);
    memset (ptr->wind_gust, '\0', 3);
    memset (ptr->wind_unit, '\0', 4);
    memset (ptr->wind_var_n, '\0', 4);
    memset (ptr->wind_var_x, '\0', 4);
    memset (ptr->weather, '\0', 50);
    memset (ptr->visibility, '\0', 5);
    memset (ptr->nebul_low, '\0', 7);
    memset (ptr->nebul_med, '\0', 7);
    memset (ptr->nebul_high, '\0', 7);
    memset (ptr->temp, '\0', 4);
    memset (ptr->temp_d, '\0', 4);
    memset (ptr->pressure, '\0', 5);
    memset (ptr->reweather, '\0', 50);
    memset (ptr->day, '\0', 3);
    memset (ptr->hour, '\0', 3);
    memset (ptr->minute, '\0', 3);

    return;
}

/*
    Libèration

*/
static void freeMetarWords (char ** words/*, OutputMetar * ptr*/)
{
    int /*i,*/ id = 0;

    while (words[id] != NULL)
    {
        free (*(words+id));
        id++;
    }
    words = NULL;printf ("toto\n");

    /*for (i = 0; i < wwSize; i++)
    {
        printf ("%d %s\n", i, ww[i]);

        free (*(ww+i));
    }
    free (ww);
    ww = NULL;*/

    /* structure */
    /*free (ptr->code);
    free (ptr->weather);
    free (ptr->reweather);
    free (ptr->oaci);
    free (ptr->wind_dir);
    free (ptr->wind_speed);
    free (ptr->wind_gust);
    free (ptr->wind_unit);
    free (ptr->wind_var_n);
    free (ptr->wind_var_x);
    free (ptr->visibility);
    free (ptr->nebul_low);
    free (ptr->nebul_med);
    free (ptr->nebul_high);
    free (ptr->temp);
    free (ptr->temp_d);
    free (ptr->pressure);
    free (ptr->day);
    free (ptr->hour);
    free (ptr->minute);*/

    return;
}

/*
    Sauve le message du Metar

*/
static char **saveMetarWords (char * metar)
{
    int id = 0;
    static char * words[MAXWORD];
    char * word;
    char * separator = {" "};

    word = strtok (metar, separator);
    if (word == NULL)
        return NULL;

    /* allocation tableau dynamique */
    words[id] = (char *) malloc (sizeof (char) * (strlen (word)+1));
    strcpy (words[id], word);

    while (words[id] != NULL)
    {
        id++;
        word = strtok (NULL, separator);

        if (word != NULL)
        {
            words[id] = (char *) malloc (sizeof (char) * (strlen (word)+1));
            strcpy (words[id], word);
        }
        else
            words[id] = NULL;
    }
    return words;
}

/*

    retour :
        1 : données d'entrée vides
        2 : station vide
*/
int parserMetar (char * metar)
{
    char ** words;
    int i, id = 0;
    int level_nebulosity = 0; /* étage bas (0) -> moyen (1) -> elevé (2) */
    OutputMetar O;
    OutputMetar * ptrO = &O;

    /* élément du message en cours de décodage */
    enum grpWord {code,             /* type du message : METAR, SPECI, ... */
                    station,        /* code OACI de la station concerné */
                    obtime,         /* date et heure de l'observation (UTC) */
                    wind,           /* vent : direction, force et unité de mesure */
                    windvar,        /* vent : variabilité éventuelle de sa direction */
                    visibility,     /* brvisibilité dominante (à l'exclusion de la visibilité minimale) */
                    weather,        /* temps présent  (au moment d l'observation) */
                    nebulosity,     /* nébulosité par plafond */
                    cavok,          /* cavok (Ceiling And Visibility OK) */
                    temperature,    /* températures mesurée et de point de rosée */
                    pressure,       /* pression atmosphérique réduite au niveau de la mer, soit QNH. (Sea level pressure) */
                    reweather,      /* temps récent (typiquement la dernière heure) */

                    end             /* fin de la collecte des données */
                    } grp;

    char ww[19][3];     /* symbôles du temps présent */
    char REww[11][3];   /* symbôles du temps récent */
    const int wwSize = 19;          /* nombre de symbôles du temps présent */
    const int REwwSize = 11;        /* nombre de symbôles du temps récent */

    /* initialisation temps présent */
    /*ww = (char **) malloc (wwSize * sizeof (char[3]));
    if (ww == NULL)
        printf ("Mémoire insuffisante pour le temps présent !\n");
    else
    {
        for (i = 0; i < wwSize; i++)
            (*(ww+i)) = malloc (3*sizeof (char));*/

         /*strcpy ((*(ww+0)), "DZ");*/
        strcpy (ww[0], "DZ");   /* bruine */                    /* précipitations */
        strcpy (ww[1], "RA");   /* pluie */
        strcpy (ww[2], "SG");   /* neige en grains */
        strcpy (ww[3], "IC");   /* poudrin de glace */
        strcpy (ww[4], "PL");   /* granule de glace */
        strcpy (ww[5], "GR");   /* grêle */
        strcpy (ww[6], "GS");   /* grésil et/ou neige roulée */

        strcpy (ww[7], "BR");   /* brume */                     /* influence l'obscurité */
        strcpy (ww[8], "FG");   /* brouillard */
        strcpy (ww[9], "FU");   /* fumée */
        strcpy (ww[10], "VA");   /* cendres volcaniques */
        strcpy (ww[11], "DU");   /* poussières */
        strcpy (ww[12], "SA");   /* sable */
        strcpy (ww[13], "HZ");   /* brume sèche */

        strcpy (ww[14], "PO");   /* tourbillon poussières / sables */    /* autres */
        strcpy (ww[15], "SQ");   /* grain */
        strcpy (ww[16], "FC");   /* trombe */
        strcpy (ww[17], "SS");   /* tempête de sable */
        strcpy (ww[18], "DS");   /* tempête de poussières */
   /* }*/

    /* initialisation temps récent */
    strcpy (REww[0], "DZ");   /* bruine */                    /* précipitations */
    strcpy (REww[1], "RA");   /* pluie */
    strcpy (REww[2], "SG");   /* neige en grains */
    strcpy (REww[3], "IC");   /* poudrin de glace */
    strcpy (REww[4], "PL");   /* granule de glace */
    strcpy (REww[5], "GR");   /* grêle */
    strcpy (REww[6], "GS");   /* grésil et/ou neige roulée */

    strcpy (REww[7], "VA");   /* cendres volcaniques */      /* influence l'obscurité */

    strcpy (REww[8], "FC");   /* trombe */                   /* autres */
    strcpy (REww[9], "SS");   /* tempête de sable */
    strcpy (REww[10], "DS");   /* tempête de poussières */

    /* initialisation structure */
    initStructMetar (ptrO);

    /* découpage du message en mots */
    words = saveMetarWords (metar);

    if (words == NULL)
        return 1;

    grp = code;

    while (words[id] != NULL)
    {
        char * word;
        word = words[id];                   printf ("id = %d, word = %s\n", id, word);
/*printf (">>>%s %d\n", words[id], id); */  /*displayMetarDecoded (O);*/
        switch (grp)
        {
            case (code) :
                if (strcmp (word, "METAR") == 0 || strcmp (word, "SPECI") == 0)
                {
                    /*strcpy (ptrO->code, word);*/

                    /*for (i = 0; i < 6; i++)
                    {
                        printf ("xxx__%c\n", (char) word[i]);
                        O.code[i] = word[i];
                    }*/
                    /*O.code = word;*/
                    copyValue (O.code, word, 6);
                    /*O.code[5] = '\0';*/
                    id++;
                }
                grp = station;
                break;

            case (station) :
                if (word == NULL || strlen (word) != 4)
                {
                    freeMetarWords (words/*, ptrO*/);
                    return 2;
                }
                if (nisalpha (word, 1) != 0 && nisalnum (word+1, 3) != 0)
                {
                    /*strcpy (ptrO->oaci, word);*/
                    /*for (i = 0; i < 5; i++)
                    {
                        printf ("xxx__%c\n", (char) word[i]);
                        O.oaci[i] = word[i];
                    }
                    O.oaci[4] = '\0';*/
                    copyValue (O.oaci, word, 5);
                    id++;
                }
                else
                {
                    freeMetarWords (words/*, ptrO*/);
                    return 2;
                }
                grp = obtime;
                break;

            case (obtime):
                if (word != NULL)
                {
                    if (strlen (word) == 4 || strlen (word) == 5)
                    {
                        if (nisdigit (word, 4) && (*(word+4) == 'Z'))
                        {
                            copyValue (O.hour, word, 3);
                            copyValue (O.minute, word+2, 3);
                            /*strcpy (ptrO->hour, nsubstr (word, 2));
                            strcpy (ptrO->minute, nsubstr (word+2, 2));*/
                            id++;
                        }
                    }
                    else if (strlen (word) == 6 || strlen (word) == 7)
                    {
                        if (nisdigit (word, 4) && (*(word+6) == 'Z'))
                        {printf ("____________________>>>>>>%s<<<<\n", "toto");
                            /*strcpy (ptrO->day, nsubstr (word, 2));*/
                            /*char * tmp;*/
                            copyValue (O.day, word, 3);
                            /*strcpy (ptrO->day, &tmp);*/
                            printf ("____________________>>>>>>%s<<<<\n", O.day);
                            /*strcpy (ptrO->hour, nsubstr (word+2, 2));*/
                            copyValue (O.hour, word+2, 3);
                            printf ("____________________>>>>>>%s<<<<\n", O.hour);
                            /*strcpy (ptrO->minute, nsubstr (word+4, 2));*/
                            copyValue (O.minute, word+4, 3);
                            printf ("____________________>>>>>>%s<<<<\n", O.minute);
                            id++;
                        }
                    }
                }
                grp = wind;
                break;

            case (wind):
                if (strlen (word) >= 7 && strchr (word, 'V') == NULL)
                {
                    /* unité*/
                    int len = strlen (word)-1;
                    if (nisalpha (word+len-3, 1))
                        /*strcpy (ptrO->wind_unit, "MPS");*/
                        copyValue (O.wind_unit, "MPS", 4);
                    else if ((*(word+len) == 'H'))
                        /*strcpy (ptrO->wind_unit, "KMH");*/
                        copyValue (O.wind_unit, "KMH", 4);
                    else if ((*(word+len) == 'T'))
                        /*strcpy (ptrO->wind_unit, "KT");*/
                        copyValue (O.wind_unit, "KT", 4);
                    else
                        /*strcpy (ptrO->wind_unit, "UNK\0");*/ /* unknown */
                        copyValue (O.wind_unit, "UNK", 4);

                    /* direction */
                    /*char dir[4];*/
                    if (nisdigit (word, 3))
                        copyValue (O.wind_dir, word, 4);
                        /*strcpy (dir, nsubstr (word, 3));
printf ("____________________>>>>>>%s<<<<\n", nsubstr (word, 3));*/
                    /* vitesse */
                    if (nisdigit (word+2, 2))
                        copyValue (O.wind_speed, word+3, 3);
                        /*strcpy (speed, nsubstr (word+2, 2));*/

                    /* rafale */
                    if ((*(word+5) == 'G') && nisdigit (word+6, 2))
                        copyValue (O.wind_gust, word+6, 3);
                        /*strcpy (gusting, nsubstr (word+4, 2));*/

                    /*if (strcmp (unit, "UNK") == 0)
                    {
                        grp = windvar;
                        break;
                    }
                    strcpy (ptrO->wind_dir, dir);
                    strcpy (ptrO->wind_speed, speed);
                    strcpy (ptrO->wind_gust, gusting);
                    strcpy (ptrO->wind_unit, unit);*/
                    grp = windvar;
                    id++;
                    break;
                }

            /* variabilité du vent dans sa direction */
            case (windvar):
                if (strlen (word) == 7 && (*(word+3) == 'V'))
                {
                    if (nisdigit (word, 3) && nisdigit (word+4, 3))
                    {
                        copyValue (O.wind_var_n, word, 4);
                        copyValue (O.wind_var_x, word+4, 4);
                        /*strcpy (ptrO->wind_var_n, nsubstr (word, 3));
                        strcpy (ptrO->wind_var_x, nsubstr (word+4, 3));*/
                        id++;
                        grp = visibility;
                        break;
                    }
                }

            /* visibilité dominante (à l'exclusion de la visibilité minimale) */
            case (visibility):
                /* visibilité "simple", sans indication de direction */
                if (strlen (word) == 4 && nisdigit (word, 4))
                {
                    copyValue (O.visibility, word, 5);
                    id++;
                    grp = weather;
                    break;
                }
                /* avec NDV */
                else if (strlen (word) == 7 && nisdigit (word, 4) && (*(word+4) == 'N') && (*(word+5) == 'D') && (*(word+6) == 'V'))
                {
                    copyValue (O.visibility, word, 5); /* garde pas l'indication NDV */
                    id++;
                    grp = weather;
                    break;
                }

            /* temps présent : ne prend pas en compte l'éloignement de la station (prefixe VC) */
            case (weather):
            {
                int weatherOK = 0;
                for (i = 0; i < wwSize; i++)
                {
                    /*if (strstr (word, (*(ww+i))) != NULL)*/
                    if (strstr (word, ww[i]) != NULL)
                    {
                        if (strchr (word, '+') != NULL && weatherOK == 0)           /* intensité forte */
                            copyPositiveValue (O.weather, "", 0);
                        else if (strchr (word, '-') != NULL && weatherOK == 0)      /* intensité faible */
                            copyNegativeValue (O.weather, "", 0);
                        if (strcmp (ww[i], "RA") == 0 || strcmp (ww[i], "SN") == 0 || strcmp (ww[i], "GR") == 0 || strcmp (ww[i], "GS") == 0)
                        {
                            if (strstr (word, "SH") != NULL)    /* averse */
                                strcat (O.weather, "SH");
                            else if (strstr (word, "TS") != NULL)    /* orage */
                                strcat (O.weather, "TS");
                        }
                        addWeather (O.weather, ww[i]);
                        weatherOK = 1;
                    }
                }
                if (weatherOK == 1)
                {
                    id++;
                    grp = nebulosity;
                    break;
                }
            }

            /* nébulosité (TCU et CB éventuels ne sont pas pris en compte) */
            case (nebulosity):
                if (strcmp (word, "NSC") == 0)
                {
                    copyValue (O.nebul_low, word, 7);
                    copyValue (O.nebul_med, word, 7);
                    copyValue (O.nebul_high, word, 7);
                    if (strlen (O.weather) < 2)
                        copyValue (O.weather, "OK",7);
                    id++;
                    grp = cavok;
                    break;
                }
                else if (strlen (word) == 6) /* on exclut les octas dûs aux éventuels TCU, CB ... */
                {
                    if (strstr (word, "FEW") != NULL  || strstr (word, "SCT") != NULL || strstr (word, "BKN") != NULL || (strstr (word, "OVC") != NULL))
                    {
                        id++;
                        if (level_nebulosity == 0)
                            copyValue (O.nebul_low, word, 7);
                        else if (level_nebulosity == 1)
                            copyValue (O.nebul_med, word, 7);
                        else if (level_nebulosity == 2)
                            copyValue (O.nebul_high, word, 7);
                        else
                        {
                            grp = cavok;
                            break;
                        }
                        level_nebulosity++;
                        break;
                    }
                }
                /*else
                {
                    grp = cavok;
                }*/
               /* else
                {
                    id++;
                    grp = weather;
                    break;
                }*/

            /* CAVOK */
            case (cavok):
                if (strcmp (word, "CAVOK") == 0)
                {
                    copyValue (O.visibility, "9999", 5);
                    copyValue (O.weather, "OK", 3);
                    copyValue (O.nebul_low, "NSC", 7);
                    copyValue (O.nebul_med, "NSC", 7);
                    copyValue (O.nebul_high, "NSC", 7);
                    id++;
                    grp = temperature;
                    break;
                }
                /*grp = temperature;
                break;*/

            /* températures mesurée et du point de rosée */
            case (temperature):
                if ((strlen (word) >= 5) && (strlen (word) <= 7) && ((*(word+3) == '/') || (*(word+2) == '/')))
                {
                    int posSlash;

                    /* température mesurée */
                    if (nisdigit (word, 2))
                        copyValue (O.temp, word, 3);
                    else if (nisdigit (word+1, 2) && (*(word) == 'M'))
                        copyNegativeValue (O.temp, word+1, 2);

                    /* point de rosée */
                    posSlash = 1+searchChar (word, '/'); /* 3 ou 4 */
                    if (nisdigit (word+posSlash, 2))
                        copyValue (O.temp_d, word+posSlash, 3);
                    else if (nisdigit (word+posSlash+1, 2) && (*(word+posSlash) == 'M'))
                        copyNegativeValue (O.temp_d, word+posSlash+1, 2);

                    id ++;
                    break;
                }

            case (pressure):
                if ((*(word) == 'Q') && strlen (word) == 5)
                {
                    copyValue (O.pressure, word+1, 5);
                    id++;
                    grp = reweather;
                    break;
                }

            case (reweather):
            {
                int reweatherOK = 0;
                for (i = 0; i < REwwSize; i++)
                {
                    if ((strstr (word, "RE") != NULL) && (strstr (word, REww[i]) != NULL))
                    {
                        if (strcmp (REww[i], "RA") == 0 || strcmp (REww[i], "SN") == 0 || strcmp (REww[i], "GR") == 0 || strcmp (REww[i], "GS") == 0)
                        {
                            if (strstr (word, "SH") != NULL)    /* averse */
                                strcat (O.reweather, "SH");
                            else if (strstr (word, "TS") != NULL)    /* orage */
                                strcat (O.reweather, "TS");
                        }
                        addWeather (O.reweather, REww[i]);
                        reweatherOK = 1;
                    }
                }
                if (reweatherOK == 1)
                {
                    id++;
                    grp = end;
                    break;
                }
            }

            case (end):
            {
                id++;
                break;
            }



            default:
            /*printf (">>>%s %d\n", words[id], id);*/
                id++;
        }
    }
    displayMetarDecoded (O);
    printf ("soon exit !\n");
    freeMetarWords (words/*, ptrO*/);
    printf ("exit !\n");
    return 0;
}




int main()
{
    char metar[1024] = "METAR LFMV 241230Z AUTO 24003G25KT 190V280 9999NDV SCT023 BKN043 OVC064 +SHRADU 16/11 Q1010 RERA";

    printf ("Decode Metar console\n");
    printf ("Enter a OACI code : ");
   /* myfgets (metar, 1024);*/
    printf ("\nMETAR : %s\n", metar);
    parserMetar (metar);
    return 0;
}
