// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once

#include <sys/acl.h>
#include <sys/types.h>

#include "hashmap.h"
#include "util.h"

typedef enum {
  OPERATION_CREATE = 1 << 0,
  OPERATION_REMOVE = 1 << 1,
  OPERATION_CLEAN = 1 << 2,
} OperationMask;

typedef enum {
  // non-glob types
  CREATE_FILE = 'f',
  TRUNCATE_FILE = 'F',
  CREATE_DIRECTORY = 'd',
  TRUNCATE_DIRECTORY = 'D',
  CREATE_SUBVOLUME = 'v',
  CREATE_SUBVOLUME_INHERIT_QUOTA = 'q',
  CREATE_SUBVOLUME_NEW_QUOTA = 'Q',
  CREATE_FIFO = 'p',
  CREATE_SYMLINK = 'L',
  CREATE_CHAR_DEVICE = 'c',
  CREATE_BLOCK_DEVICE = 'b',
  COPY_FILES = 'C',

  // glob types
  SET_XATTR = 't',
  RECURSIVE_SET_XATTR = 'T',
  SET_ACL = 'a',
  RECURSIVE_SET_ACL = 'A',
  SET_ATTRIBUTE = 'h',
  RECURSIVE_SET_ATTRIBUTE = 'H',
  IGNORE_PATH = 'x',
  IGNORE_DIRECTORY_PATH = 'X',
  REMOVE_PATH = 'r',
  RECURSIVE_REMOVE_PATH = 'R',
  RELABEL_PATH = 'z',
  RECURSIVE_RELABEL_PATH = 'Z',
  ADJUST_MODE = 'm',
  WRITE_FILE = 'w',
} ItemType;

typedef enum {
  CREATION_NORMAL,
  CREATION_EXISTING,
  CREATION_FORCE,
} CreationMode;

typedef struct Item {
  ItemType type;

  char *path;
  char *argument;

  char **xattrs;
  size_t n_xattrs;

  acl_t acl_access;
  acl_t acl_access_exec;
  acl_t acl_default;

  uid_t uid;
  gid_t gid;
  mode_t mode;
  uint64_t age;
  dev_t major_minor;

  unsigned attribute_value;
  unsigned attribute_mask;

  bool uid_set;
  bool gid_set;
  bool mode_set;
  bool uid_only_create;
  bool gid_only_create;
  bool mode_only_create;
  bool age_set;
  bool mask_perms;
  bool attribute_set;

  bool keep_first_level;
  bool append_or_force;
  bool allow_failure;
  bool try_replace;

  OperationMask done;
} Item;

typedef struct ItemArray ItemArray;

struct ItemArray {
  Item *items;
  size_t n_items;

  ItemArray *parent;
  ItemArray **children;
  size_t n_children;
};

typedef struct {
  OrderedHashmap *items;
  OrderedHashmap *globs;
} Context;

// returns true for types that go into the globs hashmap
static inline bool item_type_is_glob(ItemType t) {
  switch (t) {
  case SET_XATTR:
  case RECURSIVE_SET_XATTR:
  case SET_ACL:
  case RECURSIVE_SET_ACL:
  case SET_ATTRIBUTE:
  case RECURSIVE_SET_ATTRIBUTE:
  case IGNORE_PATH:
  case IGNORE_DIRECTORY_PATH:
  case REMOVE_PATH:
  case RECURSIVE_REMOVE_PATH:
  case RELABEL_PATH:
  case RECURSIVE_RELABEL_PATH:
  case ADJUST_MODE:
  case WRITE_FILE:
    return true;
  default:
    return false;
  }
}

void item_free(Item *i);
void item_array_free(ItemArray *a);

static inline void item_array_freep(ItemArray **ap) {
  if (*ap)
    item_array_free(*ap);
}

void context_init(Context *c);
void context_done(Context *c);

ItemArray *context_lookup(Context *c, const char *path);

extern bool arg_boot;
extern bool arg_user_mode;
extern char **arg_include_prefixes;
extern size_t n_include_prefixes;
extern char **arg_exclude_prefixes;
extern size_t n_exclude_prefixes;

int parse_config_file(Context *c, const char *path);
int parse_config_files(Context *c, const char *const *config_dirs, int argc,
                       char **argv);

extern int parse_attribute_from_arg(Item *item);

void context_link_parents(Context *c);
