#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

#define BUF_SIZE 8192
#define BUF_FILL_FORMAT "%8192c"
#define CLIENT_LOCK_PATH "./ipc/client.lock"
#define SERVER_LOCK_PATH "./ipc/server.lock"
#define SERVER_ENVIRONMENT_PATH "./ipc/server.environment"
#define SERVER_STDIN_PATH "./ipc/server.stdin"
#define SERVER_STDOUT_PATH "./ipc/server.stdout"
#define SERVER_STDERR_PATH "./ipc/server.stderr"
#define SERVER_RB "./server.rb"

static int argc;
static char **argv;
static char **env;
static FILE *fs_client_lock;
static FILE *fs_server_lock;
static FILE *fs_environment;
static FILE *fs_stdin;
static FILE *fs_stderr;
static FILE *fs_stdout;
static struct flock lock;
static char **e;
static char buf[BUF_SIZE];
static int n;

static void
ensure_server_running ()
{
  int ret;
  char *cmd;
  fs_server_lock = fopen (SERVER_LOCK_PATH, "w");
  lock.l_type = F_WRLCK;
  ret = fcntl (fileno (fs_server_lock), F_SETLK, &lock);
  if (ret == 0)
    {
      cmd = "nohup ruby " SERVER_RB " </dev/null >/dev/null 2>&1 &";
      system (cmd);
      lock.l_type = F_UNLCK;
      fcntl (fileno (fs_server_lock), F_SETLKW, &lock);
    }
}
static void
aquire_lock ()
{
  fs_client_lock = fopen (CLIENT_LOCK_PATH, "w");
  lock.l_type = F_WRLCK;
  fcntl (fileno (fs_client_lock), F_SETLKW, &lock);
}
static void
send_env ()
{
  fs_environment = fopen (SERVER_ENVIRONMENT_PATH, "w");
  for (e = env; *e; e++)
    {
      fprintf (fs_environment, "%s%c", *e, '\0');
    }
  fclose (fs_environment);
}
static void
send_stdin ()
{
  fs_stdin = fopen (SERVER_STDIN_PATH, "w");
  while ((n = fscanf (stdin, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (fs_stdin, "%*s", n, buf);
    }
  fclose (fs_stdin);
}
static void
receive_stderr ()
{
  fs_stderr = fopen (SERVER_STDERR_PATH, "r");
  while ((n = fscanf (fs_stderr, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (stderr, "%*s", n, buf);
    }
  fclose (fs_stderr);
}
static void
receive_stdout ()
{
  fs_stdout = fopen (SERVER_STDOUT_PATH, "r");
  while ((n = fscanf (fs_stdout, BUF_FILL_FORMAT, &buf[0])) > 0)
    {
      fprintf (stdout, "%*s", n, buf);
    }
  fclose (fs_stdout);
}
static void
release_lock ()
{
  lock.l_type = F_UNLCK;
  fcntl (fileno (fs_client_lock), F_SETLK, &lock);
}

int
main (__argc, __argv, __env)
     int __argc;
     char **__argv;
     char **__env;
{
  argc = __argc;
  argv = __argv;
  env = __env;

  ensure_server_running ();
  aquire_lock ();
  send_env ();
  //send_stdin ();
  //receive_stderr ();
  receive_stdout ();
  release_lock ();
  return 0;
}
