#include <alias/file_system.h>
#include <alias/glob.h>

#include "uv_local.h"

#define READDIR_SIZE 32


struct FileExists {
  uv_fs_t req;
  void (*f)(void *ud, bool value);
  void *ud;
};

static void FileExists_stat(uv_fs_t *req) {
  struct FileExists *state = (struct FileExists *)uv_req_get_data((uv_req_t *)req);
  state->f(state->ud, state->req.result >= 0);
  uv_fs_req_cleanup(req);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

void alias_file_exists(alias_str path, void (*f)(void *ud, bool value), void *ud) {
  struct FileExists *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->f = f;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req.data, state);
  uv_fs_stat(alias_libuv_event_loop(), &state->req, path, FileExists_stat);
}

bool alias_file_exists_synchronous(alias_str path) {
  uv_fs_t req;
  uv_fs_stat(alias_libuv_event_loop(), &req, path, NULL);
  bool result = req.result >= 0;
  uv_fs_req_cleanup(&req);
  return result;
}

struct FileFind {
  uv_fs_t req;
  uv_dir_t *dir;
  uv_dirent_t dirents[READDIR_SIZE];
  alias_str pattern;
  void (*error)(void *ud);
  void (*found)(void *ud, alias_str found_path);
  void (*done)(void *ud);
  void *ud;
};

static void FileFind_fs_closedir(uv_fs_t *req) {
  struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
  uv_fs_req_cleanup(req);
  state->done(state->ud);
  alias_str_free(NULL, state->pattern);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

static void FileFind_fs_readdir(uv_fs_t *req) {
  struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
  if(req->result <= 0) {
    uv_fs_req_cleanup(req);
    uv_fs_closedir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_closedir);
    return;
  }
  uint32_t count = req->result;
  for(uint32_t i = 0; i < count; i++) {
    if(state->dirents[i].name == NULL) {
      continue;
    }
    if(!alias_glob_match(state->pattern, state->dirents[i].name)) {
      continue;
    }
    state->found(state->ud, state->dirents[i].name);
  }
  state->dir->dirents = state->dirents;
  state->dir->nentries = READDIR_SIZE;
  uv_fs_req_cleanup(req);
  uv_fs_readdir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_readdir);
}

static void FileFind_fs_opendir(uv_fs_t *req) {
  struct FileFind *state = (struct FileFind *)uv_req_get_data((uv_req_t *)req);
  if(req->result < 0) {
    uv_fs_req_cleanup(req);
    state->error(state->ud);
    alias_str_free(NULL, state->pattern);
    alias_free(NULL, state, sizeof(*state), alignof(*state));
  } else {
    state->dir = (uv_dir_t *)req->ptr;
    state->dir->dirents = state->dirents;
    state->dir->nentries = READDIR_SIZE;
    uv_fs_req_cleanup(req);
    uv_fs_readdir(alias_libuv_event_loop(), &state->req, state->dir, FileFind_fs_readdir);
  }
}

void alias_file_find(alias_str path, alias_str pattern, void (*error)(void *ud),
                     void (*found)(void *ud, alias_str found_path), void (*done)(void *ud), void *ud) {
  struct FileFind *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->pattern = alias_str_clone(NULL, pattern);
  state->error = error;
  state->found = found;
  state->done = done;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req, state);
  uv_fs_opendir(alias_libuv_event_loop(), &state->req, path, FileFind_fs_opendir);
}

void alias_file_find_synchronous(alias_str path, alias_str pattern, alias_MemoryCB *mcb,
                                 struct alias_FileFindSynchronousResults *results) {}

struct FileRead {
  uv_fs_t req;
  int fd;
  void (*error)(void *ud);
  void (*partial)(void *ud, const void *data, uint64_t length);
  void (*done)(void *ud);
  void *ud;
  uint8_t buffer[1 << 12];
};

static void FileRead_fs_close_cb(uv_fs_t *req) {
  struct FileRead *state = (struct FileRead *)uv_req_get_data((uv_req_t *)req);
  uv_fs_req_cleanup(req);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

static void FileRead_fs_read_execute(struct FileRead *state);
static void FileRead_fs_read_cb(uv_fs_t *req) {
  struct FileRead *state = (struct FileRead *)uv_req_get_data((uv_req_t *)req);
  if(req->result == 0) {
    state->done(state->ud);
    uv_fs_req_cleanup(req);
    uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileRead_fs_close_cb);
  } else if(req->result < 0) {
    state->error(state->ud);
    uv_fs_req_cleanup(req);
    uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileRead_fs_close_cb);
  } else {
    state->partial(state->ud, state->buffer, req->result);
    uv_fs_req_cleanup(req);
    FileRead_fs_read_execute(state);
  }
}

static void FileRead_fs_read_execute(struct FileRead *state) {
  uv_buf_t buf;
  buf.base = (void *)state->buffer;
  buf.len = sizeof(state->buffer);
  uv_fs_read(alias_libuv_event_loop(), &state->req, state->fd, &buf, 1, -1, FileRead_fs_read_cb);
}

static void FileRead_fs_open_cb(uv_fs_t *req) {
  struct FileRead *state = (struct FileRead *)uv_req_get_data((uv_req_t *)req);
  if(state->req.result < 0) {
    state->error(state->ud);
    alias_free(NULL, state, sizeof(*state), alignof(*state));
    return;
  }
  state->fd = state->req.result;
  uv_fs_req_cleanup(req);
  FileRead_fs_read_execute(state);
}

void alias_file_read(alias_str path, void (*error)(void *ud),
                     void (*partial)(void *ud, const void *data, uint64_t length), void (*done)(void *ud), void *ud) {
  struct FileRead *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->error = error;
  state->partial = partial;
  state->done = done;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req, state);
  uv_fs_open(alias_libuv_event_loop(), &state->req, path, 0, 0, FileRead_fs_open_cb);
}

struct FileReadAll {
  uv_fs_t req;
  uv_buf_t buf;
  int fd;
  alias_MemoryCB *mcb;
  void (*error)(void *ud);
  void (*done)(void *ud, const void *data, uint64_t length);
  void *ud;
};

static void FileReadAll_fs_close_cb(uv_fs_t *req) {
  struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
  uv_fs_req_cleanup(req);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

static void FileReadAll_fs_read_cb(uv_fs_t *req) {
  struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
  if(state->req.result < 0) {
    state->error(state->ud);
    alias_free(state->mcb, state->buf.base, state->buf.len, 4);
  } else {
    state->done(state->ud, state->buf.base, state->buf.len);
    // the data is now owned by the callee, do not free
  }
  uv_fs_req_cleanup(req);
  uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_close_cb);
}

static void FileReadAll_fs_fstat_cb(uv_fs_t *req) {
  struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
  if(state->req.result < 0) {
    state->error(state->ud);
    uv_fs_req_cleanup(req);
    uv_fs_close(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_close_cb);
  } else {
    state->buf.len = state->req.statbuf.st_size;
    state->buf.base = alias_malloc(state->mcb, state->buf.len, 4);
    uv_fs_req_cleanup(req);
    uv_fs_read(alias_libuv_event_loop(), &state->req, state->fd, &state->buf, 1, 0, FileReadAll_fs_read_cb);
  }
}

static void FileReadAll_fs_open_cb(uv_fs_t *req) {
  struct FileReadAll *state = (struct FileReadAll *)uv_req_get_data((uv_req_t *)req);
  if(state->req.result < 0) {
    state->error(state->ud);
    uv_fs_req_cleanup(req);
    alias_free(NULL, state, sizeof(*state), alignof(*state));
  } else {
    state->fd = req->result;
    uv_fs_req_cleanup(req);
    uv_fs_fstat(alias_libuv_event_loop(), &state->req, state->fd, FileReadAll_fs_fstat_cb);
  }
}

void alias_file_read_all(alias_str path, alias_MemoryCB *mcb, void (*error)(void *ud),
                         void (*done)(void *ud, const void *data, uint64_t length), void *ud) {
  struct FileReadAll *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->mcb = mcb;
  state->error = error;
  state->done = done;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req.data, state);
  uv_fs_open(alias_libuv_event_loop(), &state->req, path, 0, 0, FileReadAll_fs_open_cb);
}

bool alias_file_read_all_synchronous(alias_str path, alias_MemoryCB *mcb, void **data_ptr, uint64_t *length_ptr) {
  uv_fs_t req;

  uv_fs_open(alias_libuv_event_loop(), &req, path, 0, 0, NULL);
  if(req.result < 0) {
    uv_fs_req_cleanup(&req);
    return false;
  }
  int file = req.result;
  uv_fs_req_cleanup(&req);

  uv_fs_fstat(alias_libuv_event_loop(), &req, file, NULL);
  if(req.result < 0) {
    uv_fs_req_cleanup(&req);
    return false;
  }
  *length_ptr = req.statbuf.st_size;
  uv_fs_req_cleanup(&req);

  *data_ptr = alias_malloc(mcb, *length_ptr, 4);
  uv_buf_t buf;
  buf.base = *data_ptr;
  buf.len = *length_ptr;
  bool error = false;
  uv_fs_read(alias_libuv_event_loop(), &req, file, &buf, 1, 0, NULL);
  if(req.result < 0) {
    uv_fs_req_cleanup(&req);
    alias_free(mcb, *data_ptr, *length_ptr, 4);
    error = true;
  }
  uv_fs_req_cleanup(&req);

  uv_fs_close(alias_libuv_event_loop(), &req, file, NULL);
  if(req.result < 0) {
    uv_fs_req_cleanup(&req);
    if(!error) {
      alias_free(mcb, *data_ptr, *length_ptr, 4);
    }
    return false;
  }
  uv_fs_req_cleanup(&req);

  return !error;
}

struct FileWrite {
  uv_fs_t req;
  void (*error)(void *ud);
  void (*f)(void *ud, struct alias_FileWriter *writer);
  void *ud;
};

static void FileWrite_fs_open_cb(uv_fs_t *req) {
  struct FileWrite *state = (struct FileWrite *)uv_req_get_data((uv_req_t *)req);
  if(req->result >= 0) {
    struct alias_FileWriter writer;
    writer.fd = req->result;
    uv_pipe_init(alias_libuv_event_loop(), &writer.pipe, 0);
    uv_pipe_open(&writer.pipe, writer.fd);
    state->f(state->ud, &writer);
    alias_FileWriter_close(&writer);
  } else {
    state->error(state->ud);
  }
  uv_fs_req_cleanup(req);
}

void alias_file_write(alias_str path, void (*error)(void *ud), void (*f)(void *ud, struct alias_FileWriter *writer),
                      void *ud) {
  struct FileWrite *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->error = error;
  state->f = f;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req, state);
  uv_fs_open(alias_libuv_event_loop(), &state->req, path, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0640, FileWrite_fs_open_cb);
}

bool alias_file_write_synchronous(alias_str path, struct alias_FileWriter *writer) {
  uv_fs_t req;
  uv_fs_open(alias_libuv_event_loop(), &req, path, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0640, NULL);
  if(req.result < 0) {
    uv_fs_req_cleanup(&req);
    return false;
  }
  writer->fd = req.result;
  uv_fs_req_cleanup(&req);
  uv_pipe_init(alias_libuv_event_loop(), &writer->pipe, 0);
  uv_pipe_open(&writer->pipe, writer->fd);
  return true;
}


void alias_DirectoryList_init(struct alias_DirectoryList *dir) { alias_memory_clear(dir, sizeof(*dir)); }

void alias_DirectoryList_free(struct alias_DirectoryList *dir, alias_MemoryCB *mcb) {
  for(uint32_t i = 0; i < dir->paths.length; i++) {
    if(dir->paths.data[i].path != NULL) {
      alias_str_free(mcb, dir->paths.data[i].path);
    }
  }
  alias_Vector_free(&dir->paths, mcb);
}

uint32_t alias_DirectoryList_add_path(struct alias_DirectoryList *dir, alias_MemoryCB *mcb, alias_str path) {
  uint32_t index = dir->paths.length;
  alias_Vector_space_for(&dir->paths, mcb, 1);
  alias_Vector_push(&dir->paths)->path = alias_str_clone(NULL, path);
  return index;
}

void alias_DirectoryList_reset_before(struct alias_DirectoryList *dir, alias_MemoryCB *mcb, uint32_t index) {
  while(dir->paths.length > index) {
    dir->paths.length--;
    if(dir->paths.data[dir->paths.length].path != NULL) {
      alias_str_free(mcb, dir->paths.data[dir->paths.length].path);
    }
  }
}

struct DirectoryListExists {
  const struct alias_DirectoryList *dir;
  uint32_t index;
  alias_str path;
  void (*f)(void *ud, bool value);
  void *ud;
};

static void DirectoryListExists_cb(void *state_, bool value) {
  struct DirectoryListExists *state = (struct DirectoryListExists *)state_;
  if(value) {
    state->f(state->ud, true);
    goto end;
  }
  if(state->index == 0) {
    state->f(state->ud, false);
  end:
    alias_str_free(NULL, state->path);
    alias_free(NULL, state, sizeof(*state), alignof(*state));
    return;
  }
  uint32_t index = --state->index;
  if(state->dir->paths.data[index].path != NULL) {
    char *path;
    alias_str_stack_format(path, "%s/%s", state->dir->paths.data[index].path, state->path);
    alias_file_exists(path, DirectoryListExists_cb, state);
  } else {
    DirectoryListExists_cb(
        state_, state->dir->paths.data[index].exists_synchronous(state->dir->paths.data[index].ud, state->path));
  }
}

void alias_DirectoryList_exists(const struct alias_DirectoryList *dir, alias_str path, void (*f)(void *ud, bool value),
                                void *ud) {
  struct DirectoryListExists *state = (struct DirectoryListExists *)alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->dir = dir;
  state->index = dir->paths.length;
  state->path = alias_str_clone(NULL, path);
  state->f = f;
  state->ud = ud;
  DirectoryListExists_cb(state, false);
}

bool alias_DirectoryList_exists_synchronous(const struct alias_DirectoryList *dir, alias_str path) {
  uint32_t index_ = dir->paths.length;
  for(; index_;) {
    uint32_t index = --index_;
    if(dir->paths.data[index].path != NULL) {
      char *path;
      alias_str_stack_format(path, "%s/%s", dir->paths.data[index].path, path);
      if(alias_file_exists_synchronous(path)) {
        return true;
      }
    } else {
      if(dir->paths.data[index].exists_synchronous(dir->paths.data[index].ud, path)) {
        return true;
      }
    }
  }
  return false;
}

struct DirectoryListFind {
  struct alias_DirectoryList *dir;
  uint32_t index;
  alias_str pattern;
  alias_Vector(alias_str) found_set;
  void (*error)(void *ud);
  void (*found)(void *ud, alias_str found_path);
  void (*done)(void *ud);
  void *ud;
};

static void DirectoryListFind_free(struct DirectoryListFind *state) {
  alias_str_free(NULL, state->pattern);
  for(uint32_t i = 0; i < state->found_set.length; i++) {
    alias_str_free(NULL, state->found_set.data[i]);
  }
  alias_Vector_free(&state->found_set, NULL);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

static void DirectoryListFind_find_done(void *state_);

static void DirectoryListFind_find_error(void *state_) {
  DirectoryListFind_find_done(state_);
}

static int str_compare(const void *a, const void *b, void *ud) {
  return alias_str_compare((alias_str)a, (alias_str)b);
}

static void DirectoryListFind_find_found(void *state_, alias_str path) {
  struct DirectoryListFind *state = (struct DirectoryListFind *)state_;
  alias_str *entry = alias_Vector_bsearch(&state->found_set, &path, str_compare, NULL);
  if(entry == NULL) {
    state->found(state->ud, path);
    alias_Vector_space_for(&state->found_set, NULL, 1);
    *alias_Vector_push(&state->found_set) = alias_str_clone(NULL, path);
    alias_Vector_qsort(&state->found_set, str_compare, NULL);
  }
}

static void DirectoryListFind_find_done(void *state_) {
  struct DirectoryListFind *state = (struct DirectoryListFind *)state_;
  if(state->index == 0) {
    state->done(state->ud);
    DirectoryListFind_free(state);
    return;
  }
  state->index--;
  const struct alias_Directory *current = &state->dir->paths.data[state->index];
  if(current->path != NULL) {
    alias_file_find(current->path, state->pattern, DirectoryListFind_find_error, DirectoryListFind_find_found,
                    DirectoryListFind_find_done, state_);
  } else {
  }
}

void alias_DirectoryList_find(struct alias_DirectoryList *dir, alias_str pattern, void (*error)(void *ud),
                              void (*found)(void *ud, alias_str found_path), void (*done)(void *ud), void *ud) {
  struct DirectoryListFind *state = (struct DirectoryListFind *)alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->dir = dir;
  state->index = dir->paths.length;
  state->pattern = alias_str_clone(NULL, pattern);
  alias_Vector_init(&state->found_set);
  state->error = error;
  state->found = found;
  state->done = done;
  state->ud = ud;
  DirectoryListFind_find_done(state);
}

struct DirectoryListRead {
  const struct alias_DirectoryList *dir;
  uint32_t index;
  alias_str path;
  void (*error)(void *ud);
  void (*partial)(void *ud, const void *data, uint64_t length);
  void (*done)(void *ud);
  void *ud;
};

static void DirectoryListRead_free(struct DirectoryListRead *state) {
  alias_str_free(NULL, state->path);
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

static void DirectoryListRead_file_read_error_cb(void *state_) {
  struct DirectoryListRead *state = (struct DirectoryListRead *)state_;
  state->error(state->ud);
  DirectoryListRead_free(state);
}

static void DirectoryListRead_file_read_partial_cb(void *state_, const void *data, uint64_t length) {
  struct DirectoryListRead *state = (struct DirectoryListRead *)state_;
  state->partial(state->ud, data, length);
}

static void DirectoryListRead_file_read_done_cb(void *state_) {
  struct DirectoryListRead *state = (struct DirectoryListRead *)state_;
  state->done(state->ud);
  DirectoryListRead_free(state);
}

static void DirectoryListRead_exists_cb(void *state_, bool value) {
  struct DirectoryListRead *state = (struct DirectoryListRead *)state_;
  const struct alias_Directory *current;
  if(value) {
    current = &state->dir->paths.data[state->index];
    if(current->path != NULL) {
      char *path;
      alias_str_stack_format(path, "%s/%s", current->path, state->path);
      alias_file_read(path, DirectoryListRead_file_read_error_cb, DirectoryListRead_file_read_partial_cb,
                      DirectoryListRead_file_read_done_cb, state_);
    } else {
      void *ptr;
      uint64_t length;
      current->read_all_synchronous(current->ud, state->path, NULL, &ptr, &length);
      state->partial(state->ud, ptr, length);
      alias_free(NULL, ptr, length, 4);
      state->done(state->ud);
      DirectoryListRead_free(state);
    }
  } else {
    state->index--;
    current = &state->dir->paths.data[state->index];
    if(current->path != NULL) {
      char *path;
      alias_str_stack_format(path, "%s/%s", current->path, state->path);
      alias_file_exists(path, DirectoryListRead_exists_cb, state_);
    } else {
      DirectoryListRead_exists_cb(state_, current->exists_synchronous(current->ud, state->path));
    }
  }
}

void alias_DirectoryList_read(const struct alias_DirectoryList *dir, alias_str path, void (*error)(void *ud),
                              void (*partial)(void *ud, const void *data, uint64_t length), void (*done)(void *ud),
                              void *ud) {
  struct DirectoryListRead *state = (struct DirectoryListRead *)alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->dir = dir;
  state->index = dir->paths.length;
  state->path = alias_str_clone(NULL, path);
  state->error = error;
  state->partial = partial;
  state->done = done;
  DirectoryListRead_exists_cb(state, false);
}

void alias_DirectoryList_read_all(const struct alias_DirectoryList *dir, alias_str path, void (*error)(void *ud),
                                  void (*done)(void *ud, const void *data, uint64_t length), void *ud) {}

bool alias_DirectoryList_read_all_synchronous(const struct alias_DirectoryList *dir, alias_str path, void **data_ptr,
                                              uint64_t *length_ptr) {
  return false;
}

void alias_DirectoryList_write(const struct alias_DirectoryList *dir, alias_str path, struct alias_FileWriter *writer) {
}


struct FileWriterWrite {
  uv_write_t req;
  void (*error)(void *ud);
  void (*done)(void *ud);
  void *ud;
};

static void FileWriterWrite_write_cb(uv_write_t *req, int status) {
  struct FileWriterWrite *state = (struct FileWriterWrite *)uv_req_get_data((uv_req_t *)req);
  if(status < 0) {
    state->error(state->ud);
  } else {
    state->done(state->ud);
  }
  alias_free(NULL, state, sizeof(*state), alignof(*state));
}

void alias_FileWriter_write(struct alias_FileWriter *writer, const void *data, uint64_t length, void (*error)(void *ud),
                            void (*done)(void *ud), void *ud) {
  uv_buf_t buf;
  buf.base = (void *)data;
  buf.len = length;
  struct FileWriterWrite *state = alias_malloc(NULL, sizeof(*state), alignof(*state));
  state->error = error;
  state->done = done;
  state->ud = ud;
  uv_req_set_data((uv_req_t *)&state->req, state);
  uv_write(&state->req, (uv_stream_t *)&writer->pipe, &buf, 1, FileWriterWrite_write_cb);
}

bool alias_FileWriter_write_synchronous(struct alias_FileWriter *writer, const void *data, uint64_t length) {
  uv_buf_t buf;
  buf.base = (void *)data;
  buf.len = length;
  while(buf.len > 0) {
    int result = uv_try_write((uv_stream_t *)&writer->pipe, &buf, 1);
    if(result == UV_EAGAIN) {
      continue;
    }
    if(result < 0) {
      return false;
    }
    if(result >= buf.len) {
      break;
    }
    buf.base += result;
    buf.len -= result;
  }
  return true;
}

static void FileWriterClose_fs_close(uv_fs_t *req) { alias_free(NULL, req, sizeof(*req), alignof(*req)); }

static void FileWriterClose_close(uv_handle_t *handle) {
  struct alias_FileWriter *writer = (struct alias_FileWriter *)uv_handle_get_data(handle);
  uv_fs_t *req = (uv_fs_t *)alias_malloc(NULL, sizeof(*req), alignof(*req));
  uv_fs_close(alias_libuv_event_loop(), req, writer->fd, FileWriterClose_fs_close);
}

void alias_FileWriter_close(struct alias_FileWriter *writer) {
  uv_handle_set_data((uv_handle_t *)&writer->pipe, writer);
  uv_close((uv_handle_t *)&writer->pipe, FileWriterClose_close);
}