File_Async_Job_System
os_file_jobs_init()
{
  open_files_init();
  File_Async_Job_System result = file_async_jobs_init(os_file_async_work_on_job);
  return result;
}

File_Handle 
os_file_open(String path, File_Access_Flags flags_access,  File_Create_Flags flags_create)
{
  File_Handle result = {};
  
  mode_t mode = 0;
  
  s32 flags = 0;
  if (has_flag_exact(flags_access, (FileAccess_Read | FileAccess_Write)))
  {
    add_flag(flags, O_RDWR);
  }
  else
  {
    if (has_flag(flags_access, FileAccess_Read))  
    {
      add_flag(flags, O_RDONLY);
    }
    else if (has_flag(flags_access, FileAccess_Write)) 
    {
      add_flag(flags, O_WRONLY); 
    }
    else
    {
      return result;
    } 
  }
  
  // This additional argument is required if we're creating a file
  // if we want this process to be able to open it again later.
  mode_t create_flags = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
  
  switch (flags_create)
  {
    case FileCreate_New: 
    { 
      add_flag(flags, O_CREAT | O_EXCL ); 
      mode = create_flags;
    } break;
    case FileCreate_OpenExisting: { /* add_flag(flags, O_); */ } break;
    case FileCreate_CreateAlways:
    case FileCreate_OpenAlways:
    { 
      add_flag(flags, O_CREAT); 
      mode = create_flags;
    } break;
    invalid_default_case;
  }
  
  s32 file_handle = open((char*)path.str, flags, mode);
  if (file_handle >= 0)
  {
    result = open_files_put_handle(file_handle, path);
  }
  else
  {
    s32 errsv = errno;
    printf("Error: os_file_open - %d - ", errsv);
    switch (errsv) {
      case EACCES: printf("EACCESS\n"); break;
      case EBADF: printf("EBADF\n"); break;
      case EBUSY: printf("EBUSY\n"); break;
      case EDQUOT: printf("EDQUOT\n"); break;
      case EEXIST: printf("EEXIST\n"); break;
      case EFAULT: printf("EFAULT\n"); break;
      case EFBIG: printf("EFBIG\n"); break;
      case ENOMEM: printf("ENOMEM\n"); break;
      case ENFILE: printf("ENFILE\n"); break;
      case EINVAL: printf("EINVAL\n"); break;
      default: printf("Unregistered error\n"); break;
    }
    printf("\tAttempting to open: %.*s\n", str_varg(path));
    printf("\tFlags: %u %u\n", flags_access, flags_create);
  }
  return result;
}

void        
os_file_close(File_Handle file_handle)
{
  s32 os_handle = open_files_get_handle(file_handle);
  if (close(os_handle) != -1)
  {
    open_files_rem_file(file_handle);
  }
  else
  {
    s32 errsv = errno;
    printf("Error: os_file_close - %d\n", errsv);
  }
}

bool
os_file_delete(String file_path)
{
  if (remove((char*)file_path.str) != 0) {
    perror("Error deleting file\n");
    return false;
  }
  return true;
}

File_Info   
os_file_get_info(File_Handle file_handle, Allocator* allocator)
{
  File_Info info = {};
  s32 os_handle = open_files_get_handle(file_handle);
  if (os_handle != -1)
  {
    String path = open_files_get_path(file_handle);
    
    struct stat os_info = {};
    if (fstat(os_handle, &os_info) != -1)
    {
      info.path = string_copy(path, allocator);
      info.path_abs = allocator_alloc_string(allocator, PATH_MAX);
      if (realpath((char*)path.str, (char*)info.path_abs.str) != 0)
      {
        info.path_abs.len = c_str_len((char*)info.path_abs.str);
      }
      else
      {
        s32 errsv = errno;
        printf("Error - os_file_get_info - %d - realpath\n", errsv);
      }
      info.size = (u64)os_info.st_size;
      if (S_ISDIR(os_info.st_mode))
      {
        add_flag(info.flags, FileFlag_IsDir);
      }
      else if (!S_ISREG(os_info.st_mode))
      {
        printf("Error - os_file_get_info - stat-ing a handle that is not a directory or a file\n");
      }
    }
    else
    {
      s32 errsv = errno;
      printf("Error: os_file_get_info - %d\n", errsv);
    }
  }
  return info;
}

Data        
os_file_read_all(File_Handle file_handle, Allocator* allocator)
{
  Data result = {};
  s32 os_handle = open_files_get_handle(file_handle);
  if (os_handle == -1) return result;
  
  // get file size
  s32 offset = lseek(os_handle, 0, SEEK_END);
  if (offset == -1) 
  {
    s32 errsv = errno;
    printf("Error: os_file_read_all:lseek - %d\n", errsv);
    return result;
  }
  lseek(os_handle, 0, SEEK_SET);
  
  result.base = allocator_alloc(allocator, offset + 1);
  result.size = offset + 1;
  
  s32 bytes_read = read(os_handle, result.base, result.size);
  if (bytes_read == (result.size - 1))
  {
    result.base[bytes_read] = 0; // null term
  }
  else if (bytes_read >= 0)
  {
    printf("Error: os_file_read:read - whole file not read.\n");
  }
  else if (bytes_read == -1)
  {
    s32 errsv = errno;
    printf("Error: os_file_read_all:read - %d\n", errsv);
  }
  
  return result;
}

bool
os_file_write_(s32 os_handle, Data file_data)
{
  s32 size_written = write(os_handle, file_data.base, file_data.size);
  if (size_written > 0 && size_written != file_data.size)
  {
    printf("Error: os_file_write_all:write - whole file not written\n");
    return true;
  }
  else if (size_written < 0)
  {
    osx_err_print("write");
    return false;
  }
  
  return true;
}

bool        
os_file_write_all(File_Handle file_handle, Data file_data)
{
  s32 os_handle = open_files_get_handle(file_handle);
  if (os_handle == -1) return false;
  
  lseek(os_handle, 0, SEEK_SET);
  return os_file_write_(os_handle, file_data);
}

bool
os_file_write(File_Handle file_handle, Data file_data)
{
  s32 os_handle = open_files_get_handle(file_handle);
  if (os_handle == -1) return false;
  
  return os_file_write_(os_handle, file_data);
}

String      
os_get_exe_path(Allocator* allocator)
{
  u32 needed = 0;
  _NSGetExecutablePath(0, &needed);
  
  String result = allocator_alloc_string(allocator, needed + 1);
  
  u32 cap = (u64)result.cap;
  s32 r = _NSGetExecutablePath((char*)result.str, &cap);
  if (r == 0)
  {
    result.len = cap;
    result.str[result.len] = 0;
  } 
  
  return result;
}

bool        
os_pwd_set(String path)
{
  s32 result = chdir((char*)path.str);
  if (result == -1) 
  {
    osx_err_print("chdir");
    return false;
  }
  return true;
}

#if 0
File_Info_List 
os_dir_enum(String path, Platform_Enum_Dir_Flags flags, Allocator* allocator)
{
  
}
#endif