/*
  Parallel Programming - 2026/2027

  Skeleton of an implementation of a termination detection algorithm
*/

#include <mpi.h>
#include <pthread.h>

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>

#include "global.h"
#include "control.h"
#include "util.h"

// the tag(s) of the control messages
#define CONTROL_SIGNAL 99   // --> must be distinct from BASIC_MESSAGE = 1

// type for control messages
typedef struct {
    int ping;
} control_message_t;

// Node operational states
typedef enum {
    ACTIVE_STATE,
    PASSIVE_STATE
} node_state_t;


// Shared Local State (Protected by state_mutex)
static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
static node_state_t    state       = PASSIVE_STATE;
static int             parent      = -1; // -1 indicates null/no parent
static int             deficit     = 0;  // C_i counter
static bool            is_initiator = false;


/*
  Main loop of the termination detection algorithm.

  When termination is detected, it must end (returning NULL or another
  appropriate value), which signals the main thread that the basic
  algorithm has terminated.

  The arguments of the termination detection algorithm are the ID of
  the process, the number of PROCESSES running the basic algorithm,
  and whether the process is an initiator.
*/
void *detect_termination(void *_args){
  thread_args_t *args = _args;
  int id = args->id;
  int processes = args->processes;
  bool initiator = args->initiator;

  while (true){
      pthread_mutex_lock(&state_mutex);
      
      // CHECK GLOBAL TERMINATION CONDITION (only happens at root)
      if (initiator && state == PASSIVE_STATE && deficit == 0)
      {
          trace("%d: [CONTROL] GLOBAL TERMINATION DETECTED!\n", id);
          pthread_mutex_unlock(&state_mutex);
          break; // Breaks loop, returns NULL, tells main() to shut down
      }
      
      pthread_mutex_unlock(&state_mutex);

      // Check network for incoming child signals (non-blocking with MPI_Iprobe)
      int flag = 0;
      MPI_Status status;
      MPI_Iprobe(MPI_ANY_SOURCE, CONTROL_SIGNAL, MPI_COMM_WORLD, &flag, &status);

      if (flag){
          control_message_t sig;
          MPI_Recv(&sig, sizeof(control_message_t), MPI_BYTE, status.MPI_SOURCE, 
                    CONTROL_SIGNAL, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

          pthread_mutex_lock(&state_mutex);
          deficit--;
          trace("%d: [CONTROL] Got signal from %d (New Deficit: %d)\n", 
                id, status.MPI_SOURCE, deficit);
          
          try_resolve_tree(id);
          pthread_mutex_unlock(&state_mutex);
      }
      else{
          // Sleep for 1 millisecond to prevent this while(true) loop 
          // from pinning the CPU core at 100% usage
          usleep(1000); 
      }
  }

  return NULL;
}

/*
  Called at startup of the basic algorithm in process ID.
*/
void control_basic_start_hook(int id)
{
}

/*
  Called when the basic algorithm process ID becomes passive.
*/
void control_become_passive_hook(int id)
{
}

/*
  Called when the basic algorithm process ID becomes active.
*/
void control_become_active_hook(int id)
{
}

/*
  Called when the basic algorithm process ID sends a basic message to
  process PEER.
*/
void control_basic_send_hook(int id, int peer)
{
}

/*
  Called when the basic algorithm process ID receives a basic message
  from process PEER.
*/
void control_basic_receive_hook(int id, int peer)
{
}