#include "ikarus.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <gmp.h>
#include <signal.h>

void register_handlers();
void register_alt_stack();

ikpcb* the_pcb;

/* get_option
   
   takes pointers to argc and argv and looks for the first
   option matching opt.  If one exists, it removes it from the argv
   list, updates argc, and returns a pointer to the option value.
   returns null if option is not found.
   */
char* 
get_option(char* opt, int argc, char** argv){
  int i;
  for(i=1; i<argc; i++){
    if(strcmp(opt, argv[i]) == 0){
      if((i+1) < argc){
        char* rv = argv[i+1];
        int j;
        for(j=i+2; j<argc; j++, i++){
          argv[i] = argv[j];
        }
        return rv;
      } 
      else {
        fprintf(stderr, "Error: option %s not provided\n", opt);
        exit(-1);
      }
    }
    else if(strcmp("--", argv[i]) == 0){
      return 0;
    }
  }
  return 0;
}

int
file_exists(char* filename){
  struct stat sb;
  int s = stat(filename, &sb);
  return (s == 0);
}

int
copy_fst_path(char* buff, char* x){
  int i = 0;
  while(1){
    char c = x[i];
    if ((c == 0) || (c == ':')){
      return i;
    } 
    buff[i] = c;
    i++;
  }
}

int global_exe(char* x){
  while(1){
    char c = *x;
    if(c == 0){
      return 1;
    } 
    if(c == '/'){
      return 0;
    }
    x++;
  }
}

int main(int argc, char** argv){
  char buff[FILENAME_MAX];
  char* boot_file = get_option("-b", argc, argv);
  if(boot_file){
    argc -= 2;
  }
  else if(global_exe(argv[0])){
    /* search path name */
    char* path = getenv("PATH");
    if(path == NULL){
      fprintf(stderr, "unable to locate boot file\n");
      exit(-1);
    }
    while(*path){
      int len = copy_fst_path(buff, path);
      char* x = buff + len;
      x = stpcpy(x, "/");
      x = stpcpy(x, argv[0]);
      if(file_exists(buff)){
        x = stpcpy(x, ".boot");
        boot_file = buff;
        path = "";
      }
      else {
        if(path[len]){
          path += (len+1);
        } else {
          fprintf(stderr, "unable to locate %s\n", argv[0]);
          exit(-1);
        }
      }
    }
  }
  else {
    char* x = buff;
    x = stpcpy(x, argv[0]);
    x = stpcpy(x, ".boot");
    boot_file = buff;
  }



  if(sizeof(mp_limb_t) != sizeof(int)){
    fprintf(stderr, "ERROR: limb size\n");
  }
  if(mp_bits_per_limb != (8*sizeof(int))){
    fprintf(stderr, "ERROR: bits_per_limb=%d\n", mp_bits_per_limb);
  }
  ikpcb* pcb = ik_make_pcb();
  the_pcb = pcb;
  { /* set up arg_list */
    ikp arg_list = null_object;
    int i = argc-1;
    while(i > 0){
      char* s = argv[i];
      int n = strlen(s);
      ikp str = ik_alloc(pcb, align(n+disp_string_data+1));
      ref(str, disp_string_length) = fix(n);
      strcpy((char*)str+disp_string_data, s);
      ikp p = ik_alloc(pcb, pair_size);
      ref(p, disp_car) = str + string_tag;
      ref(p, disp_cdr) = arg_list;
      arg_list = p+pair_tag;
      i--;
    }
    pcb->arg_list = arg_list;
  }
  register_handlers();
  register_alt_stack();
  ik_fasl_load(pcb, boot_file);
  /*
  fprintf(stderr, "collect time: %d.%03d utime, %d.%03d stime (%d collections)\n", 
                  pcb->collect_utime.tv_sec, 
                  pcb->collect_utime.tv_usec/1000, 
                  pcb->collect_stime.tv_sec, 
                  pcb->collect_stime.tv_usec/1000,
                  pcb->collection_id );
                  */
  ik_delete_pcb(pcb);
  return 0;
}

#if 0
     #include <signal.h>

     struct  sigaction {
             union {
                     void    (*__sa_handler)(int);
                     void    (*__sa_sigaction)(int, struct __siginfo *, void *);
             } __sigaction_u;                /* signal handler */
             int     sa_flags;               /* see signal options below */
             sigset_t sa_mask;               /* signal mask to apply */
     };

     #define sa_handler      __sigaction_u.__sa_handler
     #define sa_sigaction    __sigaction_u.__sa_sigaction

     int
     sigaction(int sig, const struct sigaction * restrict act,
         struct sigaction * restrict oact);
#endif

void handler(int signo, struct __siginfo* info, ucontext_t* uap){
  the_pcb->engine_counter = -1;
  the_pcb->interrupted = 1;
}

void
register_handlers(){
  struct sigaction sa;
  sa.sa_sigaction = (void(*)(int,struct __siginfo*,void*)) handler;
  sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
  sa.sa_mask = 0;
  int err = sigaction(SIGINT, &sa, 0);
  if(err){
    fprintf(stderr, "Sigaction Failed: %s\n", strerror(errno));
    exit(-1);
  }
}


#if 0
SYNOPSIS
     #include <sys/types.h>
     #include <signal.h>

     struct sigaltstack {
             char   *ss_sp;
             int     ss_size;
             int     ss_flags;
     };

     int
     sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss);
#endif

void
register_alt_stack(){
  char* stk = ik_mmap(SIGSTKSZ);
  if(stk == 0){
    fprintf(stderr, "Cannot maloc an alt stack\n");
    exit(-1);
  }

  struct sigaltstack sa;
  sa.ss_sp = stk;
  sa.ss_size = SIGSTKSZ;
  sa.ss_flags = 0;
  int err = sigaltstack(&sa, 0);
  if(err){
    fprintf(stderr, "Cannot set alt stack: %s\n", strerror(errno));
    exit(-1);
  }
}