#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <setjmp.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <termios.h>
#include <stdbool.h>
 
 
#include <ctx.h>
#include <cthread.h>
#include <cthread_barrier.h>
#include <cthread_once.h>
 
#include "main.h"
#include "phil.h"
 
const char *doing_str[] = {
    "  thinking..........",        
    "  wants to eat......",        
    "  wait for forks....",        
    "  eating............",        
    "  ask right fork....",        
    "  wait right fork...",        
    "  ask left fork.....",        
    "  wait left fork....",        
    "  give all forks....",        
    "  ------------------",        
    NULL
};
 
static struct cthread_barrier **barriers;
static volatile int phil_quit = 0;
 
void
phil_create_barriers(void)
{
 
    barriers = calloc(max_threads, sizeof(struct cthread_barrier *));
    if (!barriers)
        CNE_RET(
"Unable to allocate cthread_barrier structures\n");
 
}
 
 
static BOOL
forks_get(phil_info_t *phil, int my_num, 
          int solution,                  
          fork_attr_t *forks_attr)       
{
    BOOL forks_acquired = false;
    BOOL pass_turn      = false;
    int right_fork      = my_num;
    int left_fork       = (my_num + 1) % forks_attr->num_forks;
 
    
 
    phil->saved_count = 3;
 
    if (solution == TICKET_BASED) {
        
 
        if (((forks_attr->forks[right_fork].philosopher == my_num) ||
             (forks_attr->forks[right_fork].philosopher == NOBODY)) &&
            ((forks_attr->forks[left_fork].philosopher == my_num) ||
             (forks_attr->forks[left_fork].philosopher == NOBODY)))
            forks_acquired = true;
    } else { 
        
 
        if (forks_attr->forks[right_fork].status == FORK_IN_USE) {
            forks_attr->forks[right_fork].philosopher = my_num;
            pass_turn                                 = true;
            phil->saved_doing                         = PHIL_ASK_RIGHT_FORK;
        } else if ((forks_attr->forks[right_fork].philosopher != my_num) &&
                   (forks_attr->forks[right_fork].philosopher != NOBODY)) {
            pass_turn = true;
 
            phil->saved_doing = PHIL_WAIT_RIGHT_FORK;
        }
 
        if (forks_attr->forks[left_fork].status == FORK_IN_USE) {
            forks_attr->forks[left_fork].philosopher = my_num;
            pass_turn                                = true;
 
            phil->saved_doing = PHIL_ASK_LEFT_FORK;
        } else if ((forks_attr->forks[left_fork].philosopher != my_num) &
                   (forks_attr->forks[left_fork].philosopher != NOBODY)) {
            pass_turn = true;
 
            phil->saved_doing = PHIL_WAIT_LEFT_FORK;
        }
 
        
 
        if (!pass_turn) {
            forks_attr->forks[right_fork].status      = FORK_IN_USE;
            forks_attr->forks[left_fork].status       = FORK_IN_USE;
            forks_attr->forks[right_fork].philosopher = NOBODY;
            forks_attr->forks[left_fork].philosopher  = NOBODY;
 
            forks_acquired = true;
        }
    }
 
    
 
    phil->saved_count = PHIL_ASK_RIGHT_FORK;
 
    return forks_acquired;
}
 
 
static void
forks_put(phil_info_t *phil, int my_num, 
          int solution,                  
          fork_attr_t *forks_attr)       
{
    int right_fork = my_num;
    int left_fork  = (my_num + 1) % forks_attr->num_forks;
 
    
    phil->saved_count = PHIL_WAIT_RIGHT_FORK;
 
 
    if (solution == TICKET_BASED) {
        
 
        forks_attr->forks[right_fork].philosopher =
            (right_fork ? right_fork - 1 : (forks_attr->num_forks - 1));
        forks_attr->forks[left_fork].philosopher = left_fork;
 
        phil->saved_doing = PHIL_GIVE_FORK;
    } else { 
        
 
        forks_attr->forks[right_fork].status = FORK_AVAILABLE;
        forks_attr->forks[left_fork].status  = FORK_AVAILABLE;
    }
 
    
    phil->saved_count = PHIL_ASK_LEFT_FORK;
}
 
 
static void
philosopher_run(void *arg)
{
    phil_info_t *phil              = arg;
    int my_num                     = phil->idx;         
    int solution                   = phil->solution;    
    phil_attr_t *phils_attr        = phil->philos_attr; 
    fork_attr_t *forks_attr        = phil->forks_attr;  
    stats_t *pStats                = phil->stats;       
    struct cthread_barrier **b     = &barriers[
cne_id()];
 
    phils_attr->i_am_ready[my_num] = true;
 
        cne_printf(
"Failed to set private data for thread\n");
 
        return;
    }
 
 
    do {
        phil->doing = PHIL_THINKING;
 
        phil->saved_count = 0;
 
        phil->doing = PHIL_WANTS_TO_EAT;
 
        
 
        while (forks_get(phil, my_num, solution, forks_attr) == false) {
            if (solution == TICKET_BASED)
                phil->doing = PHIL_WAIT_FORK;
            if (phil->quit)
                break;
            pStats->go_hungry[my_num]++;
            phil->saved_count = 1;
        }
 
        phil->doing = PHIL_ASK_RIGHT_FORK;
 
        pStats->food_in_take[my_num]++;
        phil->saved_count = 2;
 
        forks_put(phil, my_num, solution, forks_attr);
    } while (phil->quit == 0);
}
 
 
void
phil_demo_start(void *arg)
{
    struct app_info *a = arg;
    phil_info_t *phil;
    unsigned int solution;   
    phil_attr_t philos_attr; 
    fork_attr_t forks_attr;  
    stats_t stats;           
    char name[32];
 
    
    solution = TICKET_BASED;
 
    
                                                                : a->num_threads;
 
    
    forks_attr.num_forks = philos_attr.num_phil;
 
    
    philos_attr.i_am_ready = calloc((size_t)philos_attr.num_phil, sizeof(BOOL));
    stats.food_in_take     = calloc((size_t)philos_attr.num_phil, sizeof(unsigned int));
    forks_attr.forks       = calloc((size_t)philos_attr.num_phil, sizeof(fork_t));
    stats.go_hungry        = calloc((size_t)philos_attr.num_phil, sizeof(unsigned int));
 
    if (forks_attr.forks) {
        
        for (int i = 0; i < philos_attr.num_phil; i++)
            forks_attr.forks[i].philosopher = -1;
    } else
        goto err;
 
    
 
    struct cthread_barrier **b = &barriers[id];
 
    snprintf(name, sizeof(name), "Barrier-%d", id);
 
    
    for (int i = 0; i < philos_attr.num_phil; i++) {
        phil = calloc(1, sizeof(phil_info_t));
        if (!phil)
            CNE_ERR_GOTO(error, 
"Unable to allocate philosopher structure %d\n", i);
 
        phil->doing       = PHIL_THINKING;
        phil->idx         = i;
        phil->solution    = solution;
        phil->philos_attr = &philos_attr;
        phil->forks_attr  = &forks_attr;
        phil->stats       = &stats;
        snprintf(name, sizeof(name), "Philosopher-%d", i);
        if (!phil->cthd)
            CNE_RET(
"Unable to start philosopher cthread %d\n", i);
 
    }
 
 
 
    do {
    } while (phil_quit == 0);
 
error:
    
err:
    free(philos_attr.i_am_ready);
    free(stats.food_in_take);
    free(stats.go_hungry);
    free(forks_attr.forks);
}
 
void
phil_demo_quit(void)
{
    phil_quit = 1;
}
CNDP_API int cne_id(void)
 
CNDP_API int cne_max_threads(void)
 
#define CNE_ERR_GOTO(lbl,...)
 
CNDP_API int cne_printf(const char *fmt,...)
 
CNDP_API int cthread_barrier_wait(struct cthread_barrier *b)
 
CNDP_API int cthread_barrier_destroy(struct cthread_barrier *b)
 
CNDP_API int cthread_mutex_destroy(struct cthread_mutex *mutex)
 
CNDP_API int cthread_mutex_lock(struct cthread_mutex *mutex)
 
CNDP_API int cthread_barrier_init(const char *name, struct cthread_barrier **barr, unsigned count)
 
CNDP_API int cthread_mutex_unlock(struct cthread_mutex *mutex)
 
CNDP_API struct cthread * cthread_create(const char *name, cthread_func_t func, void *arg)
 
CNDP_API int cthread_set_thread_private(struct cthread *c, void *arg)
 
CNDP_API int cthread_mutex_init(const char *name, struct cthread_mutex **mutex, const struct cthread_mutexattr *attr)
 
CNDP_API void cthread_sleep_msec(uint64_t ms)