/*!
 * \file main.c
 * This is the main file of the RTES final task.
 *
 * \author Christos Choutouridis AEM:8997 <cchoutou@ece.auth.gr>
 */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include<signal.h>
#include <unistd.h>
#include <pthread.h>

#include "listener.h"
#include "client.h"


/*!
 * Global data
 */
//! @{

settings_t  settings_init (settings);  //!< Application settings
devList_t   devList[AEMLIST_SIZE];     //!< Device list
stats_t     stats;                     //!< Statistical data

//! @}

/*!
 * CLI short options
 */
const char *short_opt = "v:i:m:M:p:s:w:th";

/*!
 * CLI long options
 */
const struct option long_opt[] = {
   {"outlevel",      required_argument,   NULL, 'v'},
   {"interval",      required_argument,   NULL, 'i'},
   {"msgintervalmin", required_argument,  NULL, 'm'},
   {"msgintervalmax", required_argument,  NULL, 'M'},
   {"pingtimeout",   required_argument,   NULL, 'p'},
   {"sendtimeout",   required_argument,   NULL, 's'},
   {"who",           required_argument,   NULL, 'w'},
   {"tracktime",     no_argument,         NULL, 't'},
   {"help",          no_argument,         NULL, 'h'},
   {NULL,            0,                   NULL, 0}
};

/*!
 * \brief
 *    Parse input argument and fill the kcli_input_t struct
 * \param   s     Pointer to settings_t data to fill
 * \param   argc  The argument count as passed to the main()
 * \param   argv  Argument array as passed to the main()
 * \return  The status of the operation
 *    \arg  0  Success
 *    \arg  1  Fail
 */
int parse_args (settings_t *s, int argc, char const *argv[]) {
   int c;

   while ((c = getopt_long (argc, (char *const *)argv, short_opt, long_opt, NULL)) != -1) {
      switch (c) {
         case -1:       /* no more arguments */
         case 0:        /* long options toggles */
            break;
         case 'v':
            s->outLevel = atoi (optarg);
            if (s->outLevel >= OUTLEVEL_2)   s->outLevel = OUTLEVEL_2;
            if (s->outLevel < OUTLEVEL_0)    s->outLevel = OUTLEVEL_0;
            break;
         case 'i':   s->seekerInterval = atoi (optarg); break;
         case 'm':   s->msgIntervalMin = atoi (optarg); break;
         case 'M':   s->msgIntervalMax = atoi (optarg); break;
         case 'p':   s->pingTimeout = atoi (optarg); break;
         case 's':   s->sendTimeout.tv_sec = atoi (optarg); break;
         case 'w':   s->me = atoi (optarg); break;
         case 't':   s->trackTime = true; break;
         case 'h':
            printf ("Syntax:\n");
            printf ("rtes_final [-t] [-h] [-v num] [-i num] [-m num] [-M num] [-p num] [-s num] [-w num]\n\n");
            printf ("-v, --outlevel num:       Change the verbosity of the program, num can be 0, 1 or 2\n");
            printf ("-i, --interval sec:       Set the interval of the seeker in [sec]\n");
            printf ("-m, --msgintervalmin sec: Set the interval of the client in [sec]\n");
            printf ("-M, --msgintervalmax sec: Set the interval of the client in [sec]\n");
            printf ("-p, --pingtimeout sec:    Set the ping timeout in [sec]\n");
            printf ("-s, --sendtimeout sec:    Set the connect/send timeout in [sec]\n");
            printf ("-w, --who AEM:            Select the AEM of the device\n");
            printf ("-t, --tracktime:          Enables time tracking in statistics\n");
            printf ("-h, --help:               Print this and exit\n");
            exit(1);
         case ':':
         default:
         case '?':
            fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c);
            fprintf (stderr, "Try `%s --help' for more information.\n", argv[0]);
            exit(1);
      }
   }
   return 0;
}

/*!
 * Main function
 */
int main (int argc, char const *argv[])  {

   // get command line arguments
   parse_args (&settings, argc, argv);

   // Initialize all subsystems
   log_init ();
   stats_init (&stats);
   devList_init (devList);
   msgList_init (&msgList);

   // Create threads
   pthread_t ptL, ptS, ptC;
   pthread_create (&ptL, NULL, pthListener, NULL);
   pthread_create (&ptS, NULL, pthSeeker, NULL);
   pthread_create (&ptC, NULL, pthClient, NULL);

   // block here
   pthread_join (ptL, NULL);
   pthread_join (ptS, NULL);
   pthread_join (ptC, NULL);
   return 0;
}
