#include <cstdio>
#include <string>

extern "C" FILE *fopen(const char *filename, const char *mode) {

  bool r = false, w = false, a = false, p = false, x = false;
  for (size_t i = 0; mode[i] != '\0'; ++i)
         if (mode[i] == 'r') r = true;
    else if (mode[i] == 'w') w = true;
    else if (mode[i] == 'a') a = true;
    else if (mode[i] == 'p') p = true;
    else if (mode[i] == 'x') x = true;

  euler::syscall::stream_handle handle;
  if (euler::syscall::open_file(filename, !r, x, handle) !=
      euler::syscall::stream_result::success)
    return 0;

  if (w && euler::syscall::set_stream_length(handle, 0) !=
           euler::syscall::stream_result::success)
      return 0;

  if (a && euler::syscall::seek_stream(
             handle, euler::syscall::seek_from::end, 0) !=
           euler::syscall::stream_result::success) {
    euler::syscall::close_stream(handle);
    return 0;
  }

  uint64_t length;
  if (euler::syscall::get_stream_length(handle, length) !=
      euler::syscall::stream_result::success) {
    euler::syscall::close_stream(handle);
    return 0;
  }

  return new euler::file_stream(handle, r || p, length, a ? length : 0);

}

extern "C" void fclose(FILE *stream) {
  stream->close();
  delete stream;
}

extern "C" int fseek(FILE *stream, long offset, int origin) {

  if (origin < 0 || origin > 2)
    return -1;

  return (int)stream->seek((euler::syscall::seek_from)origin, offset);

}

extern "C" size_t fread(
  void *buffer, size_t size, size_t count, FILE *stream) {

  return (stream->read(size * count, buffer).first - 1) / size + 1;

}
