#include "config.h"

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

#ifdef WIN32
#ifdef USE_SDL1
#include <SDL/SDL.h>
#endif
#ifdef USE_SDL2
#include <SDL2/SDL.h>
#endif
#endif

#ifdef USE_NLLIBC
#include <nllibc.h>
#endif

#include "nlltypes.h"
#include "nlllib.h"
#include "nllmain.h"
#include "argument.h"

static void signal_init(void);

static void sigint_handler(int arg)
{
  nll_finish();
#ifdef WIN32
  signal_init();
#endif
  return;
}

#ifdef WIN32
#define USE_OLD_SIGNAL
#endif

static void signal_init(void)
{
#ifndef USE_OLD_SIGNAL
  struct sigaction sa;

  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = sigint_handler;

  sigaction(SIGINT, &sa, NULL);
#else
  signal(SIGINT, sigint_handler);
#endif
}

static void signal_reset(void)
{
#ifndef USE_OLD_SIGNAL
  struct sigaction sa;

  memset(&sa, 0, sizeof(sa));
  sa.sa_handler = SIG_DFL;

  sigaction(SIGINT, &sa, NULL);
#else
  signal(SIGINT, SIG_DFL);
#endif
}

static void init(void)
{
  int r;

  signal_init();

  if ((r = nll_init(stdin, stdout)) < 0)
    NLL_ERREXIT(r, "(EXIT)");
}

static void done(void)
{
  int r;

  if ((r = nll_done()) < 0)
    NLL_ERREXIT(r, "(EXIT)");

  signal_reset();
}

static void help(void)
{
  printf("nll [-help|-h] [-version|-v] [-quit|-q] [-number|-n] [-no-run|-nr] [-no-argv|-na] [-no-system|-ns] [-no-syscall|-nc] [-no-network|-nn] [-no-fixed|-nf] [-e <command>] [<filename>|-|{--|stdin}] [<arguments>...]\n");
  printf("\t-help,-h\tOutput help message.\n");
  printf("\t-version,-v\tOutput version.\n");
  printf("\t-quit,-q\tAuto quit after execution of the input file.\n");
  printf("\t-number,-n\tAuto numbering mode of the input file.\n");
  printf("\t-no-run,-nr\tDisable auto execution of the input file.\n");
  printf("\t-no-argv,-na\tNot give ARGV[] for arguments.\n");
  printf("\t-no-system,-ns\tDisable system functions.\n");
  printf("\t-no-syscall,-nc\tDisable SYSCALL() function.\n");
  printf("\t-no-network,-nn\tDisable network functions.\n");
  printf("\t-no-fixed,-nf\tDisable one time only calculation of the fixed value.\n");
  printf("\t-e <command>\tExecute the command.\n");
  printf("\t-\t\tThe file name not to read.\n");
  printf("\t--,stdin\tInput the commands from standard input.\n");
  printf("Examples:\n");
  printf("\t$ nll sample.nll arg0 arg1 arg2\n");
  printf("\t$ echo \"abc\" | nll -e \'S=FGETS(0);FPUTS(0,S)\'\n");
  exit(0);
}

static void outver(void)
{
#ifdef NLL_VERSION
  printf("%s\n", NLL_VERSION);
#endif
  exit(0);
}

static int quit        = 0;
static int auto_number = 0;
static int auto_run    = 1;
static int enable_argv = 1;
static int no_system   = 0;
static int no_syscall  = 0;
static int no_network  = 0;
static int no_fixed    = 0;
static char *command = NULL;

static argument_t args[] = {
  { "-help"      , ARGUMENT_TYPE_FUNCTION, help         },
  { "-h"         , ARGUMENT_TYPE_FUNCTION, help         },
  { "-version"   , ARGUMENT_TYPE_FUNCTION, outver       },
  { "-v"         , ARGUMENT_TYPE_FUNCTION, outver       },
  { "-quit"      , ARGUMENT_TYPE_FLAG_ON , &quit        },
  { "-q"         , ARGUMENT_TYPE_FLAG_ON , &quit        },
  { "-number"    , ARGUMENT_TYPE_FLAG_ON , &auto_number },
  { "-n"         , ARGUMENT_TYPE_FLAG_ON , &auto_number },
  { "-no-run"    , ARGUMENT_TYPE_FLAG_OFF, &auto_run    },
  { "-nr"        , ARGUMENT_TYPE_FLAG_OFF, &auto_run    },
  { "-no-argv"   , ARGUMENT_TYPE_FLAG_OFF, &enable_argv },
  { "-na"        , ARGUMENT_TYPE_FLAG_OFF, &enable_argv },
  { "-no-system" , ARGUMENT_TYPE_FLAG_ON , &no_system   },
  { "-ns"        , ARGUMENT_TYPE_FLAG_ON , &no_system   },
  { "-no-syscall", ARGUMENT_TYPE_FLAG_ON , &no_syscall  },
  { "-nc"        , ARGUMENT_TYPE_FLAG_ON , &no_syscall  },
  { "-no-network", ARGUMENT_TYPE_FLAG_ON , &no_network  },
  { "-nn"        , ARGUMENT_TYPE_FLAG_ON , &no_network  },
  { "-no-fixed"  , ARGUMENT_TYPE_FLAG_ON , &no_fixed    },
  { "-nf"        , ARGUMENT_TYPE_FLAG_ON , &no_fixed    },
  { "-e"         , ARGUMENT_TYPE_STRING  , &command     },
  { NULL, ARGUMENT_TYPE_NONE, NULL }
};

int main(int argc, char *argv[])
{
  char *filename = NULL, *nullargv = NULL;
  int r;

  argc--;
  argv++;

  argument_read(&argc, argv, args, ARGUMENT_NOTFOUND_BREAK);

  if (no_system)
    nll_nosystem_set();
  if (no_syscall)
    nll_nosyscall_set();
  if (no_network)
    nll_nonetwork_set();
  if (no_fixed)
    nll_nofixed_set();

  if (argc > 0) {
    filename = argv[0];
  }

  init();

  if (filename) {
    if (!strcmp(filename, "-")) {
      /* none */
    } else if (!strcmp(filename, "--") || !strcmp(filename, "stdin")) {
      if ((r = nll_file_stdin()) < 0)
	NLL_ERREXIT(r, "(stdin)");
    } else {
      if ((r = nll_file_open(filename)) < 0)
	NLL_ERREXIT(r, filename);
    }
  }

  if (isatty(STDIN_FILENO))
    nll_tty_set();

  if (!enable_argv) {
    argc = 0;
    argv = &nullargv;
  }

  r = nll_main(argc, argv, quit, auto_run, auto_number, command);

  done();

  return r;
}
