#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <jni.h>

#include "exec0.h"
#include <Spawner.h>


#define DEBUGIT 0


/*
 * Header for class org_eclipse_cdt_utils_spawner_Spawner 
 */


#if DEBUGIT
static void print_array(char **c_array)
{
    if (c_array) {
        char **p = c_array;
        for (; *p; p++) {
            if (*p) {
                fprintf(stderr, " %s", *p);
            }
        }
    } else {
        fprintf(stderr, "null");
    }
    fprintf(stderr, "\n");
}
#endif


static char **alloc_c_array(JNIEnv * env, jobjectArray j_array)
{
    int i;
    jint c_array_size = (*env)->GetArrayLength(env, j_array);
    char **c_array = calloc(c_array_size + 1, sizeof(*c_array));

    if (c_array == NULL)
        return NULL;

    for (i = 0; i < c_array_size; i++) {
        jstring j_str =
            (jstring) (*env)->GetObjectArrayElement(env, j_array, i);
        const char *c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
        c_array[i] = (char *) strdup(c_str);
        (*env)->ReleaseStringUTFChars(env, j_str, c_str);
        (*env)->DeleteLocalRef(env, j_str);
    }

    return c_array;
}


static void free_c_array(char **c_array)
{
    if (c_array) {
        char **p = c_array;
        for (; *p; p++) {
            if (*p) {
                free(*p);
            }
        }
        free(c_array);
    }
}


JNIEXPORT jint JNICALL
Java_org_eclipse_cdt_utils_spawner_Spawner_exec1(JNIEnv * env, jobject jobj,
                                               jobjectArray jcmd,
                                               jobjectArray jenv,
                                               jstring jdir)
{
    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
    char **cmd;
    char **envp;
    pid_t pid = -1;

    cmd = alloc_c_array(env, jcmd);
    if (cmd == NULL)
        goto bail_out;

    envp = alloc_c_array(env, jenv);
    if (envp == NULL)
        goto bail_out;

#if DEBUGIT
    fprintf(stderr, "command:");
    print_array(cmd);
    fprintf(stderr, "Envp:");
    print_array(envp);
    fprintf(stderr, "dirpath: %s\n", dirpath);
#endif

    pid = exec0(cmd[0], cmd, envp, dirpath, NULL);
    if (pid < 0)
        goto bail_out;

  bail_out:
    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
    if (cmd)
        free_c_array(cmd);
    if (envp)
        free_c_array(envp);
    return pid;
}

/*
 * Class:     org_eclipse_cdt_utils_spawner_Spawner
 * Method:    exec0
 * Signature: ([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[I)I
 */
JNIEXPORT jint JNICALL
Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv * env, jobject jobj,
                                               jobjectArray jcmd,
                                               jobjectArray jenv,
                                               jstring jdir,
                                               jintArray jchannels)
{
    jint *channels = (*env)->GetIntArrayElements(env, jchannels, 0);
    const char *dirpath = (*env)->GetStringUTFChars(env, jdir, NULL);
    char **cmd;
    char **envp;
    int fd[3];
    pid_t pid = -1;

    if (channels == NULL)
        goto bail_out;

    cmd = alloc_c_array(env, jcmd);
    if (cmd == NULL)
        goto bail_out;

    envp = alloc_c_array(env, jenv);
    if (envp == NULL)
        goto bail_out;

#if DEBUGIT
    fprintf(stderr, "command:");
    print_array(cmd);
    fprintf(stderr, "Envp:");
    print_array(envp);
    fprintf(stderr, "dirpath: %s\n", dirpath);
#endif

    pid = exec0(cmd[0], cmd, envp, dirpath, fd);
    if (pid < 0)
        goto bail_out;

    channels[0] = fd[0];
    channels[1] = fd[1];
    channels[2] = fd[2];

  bail_out:
    (*env)->ReleaseIntArrayElements(env, jchannels, channels, 0);
    (*env)->ReleaseStringUTFChars(env, jdir, dirpath);
    if (cmd)
        free_c_array(cmd);
    if (envp)
        free_c_array(envp);
    return pid;
}

/*
 * Class:     org_eclipse_cdt_utils_spawner_Spawner
 * Method:    raise
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL
Java_org_eclipse_cdt_utils_spawner_Spawner_raise(JNIEnv * env, jobject jobj,
                                               jint pid, jint sig)
{
    int status = -1;

    switch (sig) {
    case 0:                    /* NOOP */
        status = kill(pid, 0);
	break;

    case 2:                    /* INTERRUPT */
        status = kill(pid, SIGINT);
	break;

    case 9:                    /* KILL */
        status = kill(pid, SIGKILL);
	break;

    case 15:                   /* TERM */
        status = kill(pid, SIGTERM);
	break;

    default:
        status = kill(pid, sig);        /* WHAT ?? */
	break;
    }

    return status;
}



/*
 * Class:     org_eclipse_cdt_utils_spawner_Spawner
 * Method:    waitFor
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL
Java_org_eclipse_cdt_utils_spawner_Spawner_waitFor(JNIEnv * env,
                                                 jobject jobj, jint pid)
{
    return wait0(pid);
}
