#include <stdlib.h>
#include <stdio.h>

typedef int fd;

struct fs;

struct fs_methods {
  const char* (*name)(struct fs*);
  void (*sync)(struct fs*);
  void (*release)(struct fs*);
  // fd create_file(fs*, sconst char*);
  // int write(fs*, fd, const char*, int);
  // ...
};

struct fs {
  struct fs_methods* methods;
  // common fields can live here
};


// Helper functions for convenience of calls
const char* name(struct fs* fs) { return fs->methods->name(fs); }
void sync(struct fs* fs) { return fs->methods->sync(fs); }
void release(struct fs* fs) { return fs->methods->release(fs); }

////////////////////////////////////////////////////////////
// FAT32
////////////////////////////////////////////////////////////

struct fat32_fs {
  struct fs fs;
  const char* message;
  // fat32 specific fields ...
};

const char* fat32_name(struct fs* fs) { return "fat32"; }

void fat32_sync(struct fs* f) {
  // get the real instance back:
  struct fat32_fs* instance = (struct fat32_fs*)f;
  printf("Sync FAT32: %s\n", instance->message);
}

void fat32_release(struct fs* f) { printf("releasing FAT32 fs\n"); free(f); }

// Actual VTable
static struct fs_methods fat32_methods = {
  &fat32_name, &fat32_sync, &fat32_release
};

// Constructor
struct fs* make_fat32(const char *message) {
  struct fat32_fs* this = calloc(sizeof(struct fat32_fs), 1);
  this->fs.methods = &fat32_methods;
  this->message = message;
  return &this->fs;
}


////////////////////////////////////////////////////////////
// EXT 4
////////////////////////////////////////////////////////////

struct ext4_fs {
  struct fs fs;
  int journal_size;
  // ext4 specific fields ...
};

const char* ext4_name(struct fs* fs) { return "ext4"; }

void ext4_sync(struct fs* f) {
  // get the real instance back:
  struct ext4_fs* this = (struct ext4_fs*)f;
  printf("Sync EXT4, journal size: %d\n", this->journal_size);
}

void ext4_release(struct fs* f) { printf("releasing EXT4 fs\n"); free(f); }

// Actual VTable
static struct fs_methods ext4_methods = {
  &ext4_name, &ext4_sync, &ext4_release
};

// Constructor
struct fs* make_ext4(int journal_size) {
  struct ext4_fs* this = calloc(sizeof(struct ext4_fs), 1);
  this->fs.methods = &ext4_methods;
  this->journal_size = journal_size;
  return &this->fs;
}


////////////////////////////////////////////////////////////
// Testing
////////////////////////////////////////////////////////////

void test_fs(struct fs* fs) {
  printf("============================================================\n");
  printf("fs name: %s\n", name(fs));
  sync(fs);
  release(fs);
}

int main() {
  test_fs(make_fat32(" wow, so OOP, very interfaces"));
  test_fs(make_ext4(3000000));
  return 0;
}
