#include <stdio.h>
#include "cdefs.h"
#ifdef _WIN32
  #include <windows.h>
  #include <direct.h>
  #include <string.h>
  #include <io.h>
  static char *filespec;
  static struct _finddata_t fileinfo;
  static int handle, error = 0;
#else
  #include <sys/stat.h>
  #include <stdlib.h>
  #include <dirent.h>
  static DIR *curr_dir;
  static struct dirent *de;
#endif

cint csys_Myself;
cint csys_Stderr, csys_Stdin, csys_Stdout;
cint csys_Command_line;

cint boolean_False, boolean_True, object_Undefined;

cint clib_ArgC, *clib_ArgV;
cint clib_stde, clib_stdi, clib_stdo;

void csys_init(){
  cstring* cline; int i;

  csys_Myself = object_integer_from(-1);
  csys_Stderr = object_integer_from(stderr);
  csys_Stdin = object_integer_from(stdin);
  csys_Stdout = object_integer_from(stdout);

  cline = mem_malloc((clib_ArgC+1)*4);
  for (i = 0; i < clib_ArgC; i++){
    cline[i] = object_string_from(clib_ArgV[i]);
  }
  cline[clib_ArgC] = 0;
  csys_Command_line = object_list_from(cline);
}

cint csys_Assign_file(cint port, cint filename, cint mode){
  cstring fn = object_as_string(filename);
  cstring md = object_as_string(mode);
  FILE* fi = fopen(fn, md);
  cint result;

  if (fi == 0)
    result = boolean_False;
  else{
    if (port == csys_Stderr) clib_stde = fi;
    if (port == csys_Stdin) clib_stdi = fi;
    if (port == csys_Stdout) clib_stdo = fi;
    ((cptr)port)[2] = fi;   /* store file handle in the int val of the port object */
    result = boolean_True;
  };
  mem_free(fn); mem_free(md);
  object_Dec_ref(port); object_Dec_ref(filename); object_Dec_ref(mode);
  return result;
}

void csys_Change_current_directory(cint name){
  cstring dn = object_as_string(name);
  chdir(dn);
  mem_free(dn);
  object_Dec_ref(name);
}

void csys_Close(cint port){
  FILE* fi = (FILE*)object_as_int(port); /* the int val of port stores the handle */
  fclose(fi);
  object_Dec_ref(port);
}

void csys_Create_directory(cint dirname){
  cstring dn = object_as_string(dirname);
  mkdir(dn, 7*64);  /* rwx------ */
  mem_free(dn);
  object_Dec_ref(dirname);
}

cint csys_Current_directory(){
  cstring cwd = getcwd(0,0);
  if (cwd == 0) return object_Undefined;
  return object_string_from(cwd);
}

void csys_Delete_directory(cint name){
  cstring dn = object_as_string(name);
  rmdir(dn);
  mem_free(dn);
  object_Dec_ref(name);
}

void csys_Delete_file(cint name){
  cstring fn = object_as_string(name);
  remove(fn);
  mem_free(fn);
  object_Dec_ref(name);
}

void csys_Execute_command(cint c){
  cstring cmd = object_as_string(c);
  system(cmd);
  mem_free(cmd);
  object_Dec_ref(c);
}

#ifdef _WIN32
cint csys_open_directory(cint dname){
  cstring name = object_as_string(dname);
  cstring filespec = mem_malloc(strlen(name)+3);
  cint result = boolean_True;
  strcpy(filespec, name);
  strcat(filespec, "/*");
  error = 0;
  handle = _findfirst(filespec, &fileinfo);
  if (handle == -1) result = boolean_False;
  mem_free(name); mem_free(filespec);
  object_Dec_ref(dname);
  return result;
}
#else
cint csys_open_directory(cint dname){
  cstring name = object_as_string(dname);
  cint result = boolean_True;
  curr_dir = opendir(name);
  if (curr_dir == 0) result = boolean_False;
  mem_free(name);
  object_Dec_ref(dname);
  return result;
}   
#endif

#ifdef _WIN32
cint csys_next_directory_entry(){
  cstring entry_name;
  if (error) return object_Undefined;
  entry_name = mem_malloc(strlen(fileinfo.name)+1);
  strcpy(entry_name, fileinfo.name);
  error = _findnext(handle, &fileinfo);
  return object_string_from(entry_name);
}
#else
cint csys_next_directory_entry(){
  cstring entry_name;
  de = readdir(curr_dir);
  if (!de) return object_Undefined;
  entry_name = mem_malloc(strlen(de->d_name)+1);
  strcpy(entry_name, de->d_name);
  return object_string_from(entry_name);
} 
#endif

cint csys_Read_line(cint port, cint strmax){
  FILE* fi = (FILE*)object_as_int(port); /* the int val of port stores the handle*/
  cstring b = mem_malloc(object_as_int(strmax)+1);
  cstring s;
  cint result;
  if (fgets(b, object_as_int(strmax), fi) == 0)
    result = object_Undefined;
  else{
    b[object_as_int(strmax)] = 0;
    s = mem_malloc(strlen(b)+1);
    strcpy(s, b);
    result = object_string_from(s);}
  mem_free(b);
  object_Dec_ref(port); object_Dec_ref(strmax);
  return result;
}

void csys_Rename_file(cint old_name, cint new_name){
  cstring on = object_as_string(old_name);
  cstring nn = object_as_string(new_name);
  rename(on, nn);
  mem_free(on); mem_free(nn);
  object_Dec_ref(old_name); object_Dec_ref(new_name);
}

void csys_Terminate_process(cint process, cint code){
  exit(object_as_int(code)); /* eventually handle other processes */
}

void csys_Write(cint port, cint str){
  FILE* fi = (FILE*)object_as_int(port);
  cstring b = object_as_string(str);
  fwrite(b, 1, strlen(b), fi);
  mem_free(b);
  object_Dec_ref(port); object_Dec_ref(str);
}

