
#include<stdarg.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<pthread.h>
#include<limits.h>
#include<sys/types.h>
#include<signal.h>

#ifdef ___PROCESS
int ___debug = 0; // value determined by parameters of main function
#else
extern int ___debug; // corresponds to variable of supervisor
#endif

#include"spcommon.h"//--- ANY change on this line MUST be reflected in BOTH SupervisorTemplate.c and filltmpl.lex, in order that filltmpl.lex may be able to replace this line with the adequate include directive

static void* ___main_function(void* ___v);

#ifdef ___PROCESS
inline void ___pr_send_signal() {
#ifndef SINGLE_READ_CHANNEL
#ifdef ___SIGNALS
  kill(getppid(), SIGUSR1);
#endif
#endif
}
#endif

// +++++++ OTHER DECLARATIONS (GENERATED CODE HERE) 


#ifndef ___NTR

// Here are some default definitions allowing to compile the file without
// automatically generated code. This is for debugging purposes.

#define ___NTR 1 // ___NTR defined in the automatically generated code
// TR represents a list of transitions and NTR its max size.
static const char ___TRinfo[10];

#endif


// ++++++++++++++++++++++++++ USER CODE--DECLARATIONS (GENERATED CODE HERE) 


static void* ___main_function(void* ___v) {
  int ___i, ___j,  ___flag, ___ntran;
  time_t ___ctime;
  char* ___str;
  struct ___thread_data *___pthr = ___v;
  struct ___trans ___TR[___NTR];

  void *p___ = 0; // Reserved for user code. Useful when there are
  // several thread copies running in parallel.
  // Each thread can allocate memory and associate p___ to the 
  // address, if needed.
  // At the end of the program p___ should be deallocated if nonzero!!!

  int ___place; // denotes the value of the current place
  int ___finish; // the program terminates when its value is nonzero
  sigset_t ___set; // set of signals that should be blocked

  ___finish = 0;
  ___flag = 0; 
  ___place = ___pthr->state;


  time(&___ctime);//___ctime used to initialize the random number generator
  // Next, ___ctime is modified so that different processes started at the 
  // same time do not have the same ___ctime.
  // The random number generator of the threads is initialized by supervisor.
#ifndef ___THREAD
  ___ctime += (int) getpid();
#else
  ___ctime += (int) getpid() + (int)(long)___pthr->id; // (long) avoids warning
  #ifndef SINGLE_READ_CHANNEL
  // block SIGUSR1 which may be used to notify main program of new messages
  sigemptyset(&___set);
  sigaddset(&___set, SIGUSR1);
  pthread_sigmask(SIG_BLOCK, &___set, 0);
  #endif
#endif
  srand(___ctime%64000);


  // +++++++++++++++++++ USER CODE--MAIN FUNCTION (GENERATED CODE HERE) 


  while(!___finish) {
    switch(___place) {
   
      // +++++++ case list (GENERATED CODE HERE)

    default:
      // display error message for undefined place
      debugInfo("Internal ERROR: the place %d is undefined", ___place);
      ___finish = 1;
    } // ends main switch block

    if(___finish)
      break; // exit main while loop

    //debugInfo("Calling ___NextState");
    ___place = ___NextState(___TR, ___TRinfo, ___place, ___i, &___finish, ___pthr, debugInfo);
    // at this point ___i = no of elements of ___TR
    //debugInfo("Going to place %d", ___place); // place = -1 when terminating

  } // ends main while loop

  ___send_exit_notification(___pthr);

  if(p___) free(p___);

  debugInfo("The end of the main function was reached");

  return 0;
}


#ifndef ___THREAD


void ___print_args(int argc, char* argv[], struct ___thread_data *___pthr, struct ___thread_comm *___pc) {
  int ___i, ___j;
  char *___str, *___s1 = 0, *___s2 = 0, *___s3 = 0, *___s4 = 0;

  if(___debug) {
    if(argc <= 1)
      asprintf(&___s1, "The main function was entered. The main function has no parameters.");  // This line should not be reached!
    else {
      for(___i = 0, ___j = 0; ___i < argc; ___i++)
	___j += strlen(argv[___i]);
      ___j += argc + 1;
      ___str = malloc(___j*sizeof(char));
      for(___i = 0, ___j = 0; ___i < argc - 1; ___i++) { 
	sprintf(___str + ___j, "%s ", argv[___i]);
	___j += strlen(argv[___i]) + 1;
      }
      sprintf(___str + ___j, "%s", argv[___i]);
      asprintf(&___s1, "The process was started with the following command line\n\t%s", ___str);
      free(___str);
    }
    if(___pthr || ___pc) {
      asprintf(&___s2, "\nThe following parameters were received:\n");
      if(___pthr)
	asprintf(&___s3, "id = %d debug = %d initial state = %d", (int)(long)___pthr->id, (int)___pthr->debug, (int)___pthr->state);
      if(___pc)
	asprintf(&___s4, " write end = %d", (int)___pc->pwrite);
    }
    if(!___s2) asprintf(&___s2, " ");
    if(!___s3) asprintf(&___s3, " ");
    if(!___s4) asprintf(&___s4, " ");
  }
  debugInfo("%s%s%s%s", ___s1, ___s2, ___s3, ___s4);
  free(___s1); free(___s2); free(___s3); free(___s4);
}


#define ___RETURN_IF_ERROR_1(___f, ___string) \
  if(!___f) { \
    ___print_args(argc, argv, ___pthr, ___pc);\
    debugInfo(___string); \
    free(___pthr); free(___pc); \
    return 1; \
  } 

#define ___RETURN_IF_ERROR_2(___f, ___string) \
  if(!___f) { \
    ___print_args(argc, argv, ___pthr, ___pc);\
    debugInfo(___string); \
    close(___pc->pread); close(___pc->pwrite); \
    ___send_exit_notification(___pthr); \
    free(___pthr); free(___pc); \
    return 1; \
  } 


int main(int argc, char* argv[]) {

  int ___i, ___j,  ___flag = 0, ___vplace;
  char* ___str;
  struct ___thread_data *___pthr = 0;
  struct ___thread_comm *___pc = 0;
  
  ___debug = 1; // allow messages if errors occur at the beginning

  ___pthr = calloc(1, sizeof(*___pthr));
  if(!___pthr) {
    debugInfo("Could not allocate 'struct ___thread_data' object");
    return 1;
  }
  ___pc = calloc(1, sizeof(*___pc));
  if(!___pc) {
    free(___pthr);
    debugInfo("Could not allocate 'struct ___thread_comm' object");
    return 1;
  } 

  ___flag = (argc > 1);
  if(___flag)
    ___pc->pread = atoi(argv[1]); 
    // Command line parameter from get_process_comm_data in SupervisorTemplate 
  
  ___RETURN_IF_ERROR_1(___flag, "The process exits since no command line parameter is given.\n");

  // READ PARAMETERS FROM PIPE

  ___j = ___get_msg(___pc->pread, &___pc->pwrite, sizeof(___pc->pwrite), debugInfo);
  ___RETURN_IF_ERROR_1(___j, "The process exits since 'pwrite' cannot be read from pipe.\n");
  ___j = ___get_msg(___pc->pread, &___pthr->id, sizeof(___pthr->id), debugInfo);
  ___RETURN_IF_ERROR_1(___j, "The process exits since process id cannot be read from pipe.\n");
  ___j = ___get_msg(___pc->pread, &___pthr->debug, sizeof(___pthr->debug), debugInfo);
  ___RETURN_IF_ERROR_2(___j, "The process exits since 'debug' cannot be read from pipe.\n");
  ___j = ___get_msg(___pc->pread, &___pthr->state, sizeof(___pthr->state), debugInfo); 
  ___RETURN_IF_ERROR_2(___j, "The process exits since the initial state cannot be read from pipe.\n");

  ___pthr->comm = ___pc;
  ___debug = ___pthr->debug;

  
  ___print_args(argc, argv, ___pthr, ___pc); 

  ___main_function(___pthr);

  debugInfo("The process is terminating.\n");
  
  free(___pthr);
  free(___pc);

  return 0;
}

#endif
