#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define ES2
#define MAX_PATH 1024
#define MAX_CHILDREN 1024

#ifdef ES2
void dirwalk(const char *dir, void (*fcn)(char *), bool recursive);
#endif
void invsymlink(char *);

int main(int argc, char *argv[]) {
  struct stat stbuf;

  if (argc != 2) {
    fprintf(stderr, "Usage: %s <path>\n", argv[0]);
    return -1;
  }
  if (lstat(argv[1], &stbuf) == -1) {
    fprintf(stderr, "%s: non riesco ad aprire %s\n", argv[0], argv[1]);
    fprintf(stderr, "%d: %s\n", errno, strerror(errno));
    return -1;
  }

  if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
#ifdef ES2
    dirwalk(argv[1], invsymlink, false);
#else
    fprintf(stderr, "Ma questa e' una cartella!\n";
#endif
  else {
    if (argv[1][0] == '/')
      invsymlink(argv[1]);
    else {
      char path[MAX_PATH];
      getcwd(path, MAX_PATH);
      if (strlen(path) + strlen(argv[1]) + 2 > sizeof(path))
        fprintf(stderr, "main: il nome %s/%s e' troppo lungo\n", path, argv[1]);
      else
        sprintf(path, "%s/%s", path, argv[1]);
      invsymlink(path);
    }
  }
}

#ifdef ES2
void dirwalk(const char *dir, void (*fcn)(char *), bool recursive) {
  char path[MAX_CHILDREN][MAX_PATH];
  bool isDir[MAX_CHILDREN];
  struct dirent *dp;
  DIR *dfd;
  char *const absdir = realpath(dir, NULL);
  // printf("%s\n", absdir);
  if ((dfd = opendir(dir)) == NULL) {
    fprintf(stderr, "dirwalk: non riesco ad aprire %s\n", dir);
    return;
  }
  int children;
  for (children = 0; children < MAX_CHILDREN && (dp = readdir(dfd)) != NULL;
       ++children) {
    struct stat stbuf;
    chdir(dir);
    if (lstat(dp->d_name, &stbuf) == -1) {
      fprintf(stderr, "dirwalk: non riesco ad aprire %s\n", dp->d_name);
      exit(-1);
    }
    if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") ||
        !S_ISLNK(stbuf.st_mode)) {
      --children;
      continue;
    }

    if (strlen(absdir) + strlen(dp->d_name) + 2 > sizeof(*path))
      fprintf(stderr, "dirwalk: il nome %s/%s e' troppo lungo\n", absdir,
              dp->d_name);
    else {
      sprintf(path[children], "%s/%s", absdir, dp->d_name);
    }
    isDir[children] = dp->d_type == DT_DIR;
  }
  for (int i = 0; i < children; ++i)
    if (isDir[i] && recursive)
      dirwalk(path[i], fcn, true);
    else {
      // printf("%s\n", path[i]);
      (*fcn)(path[i]);
    }
  closedir(dfd);
  free(absdir);
}
#endif

void invsymlink(char *path) {
  struct stat stbuf;

  if (lstat(path, &stbuf) == -1) {
    fprintf(stderr, "invsymlink: non riesco ad aprire %s\n", path);
    exit(-1);
  }
  if (!S_ISLNK(stbuf.st_mode))
    return;

  char *const rp = realpath(path, NULL);
  // printf("%s VS %s\n", path, rp);

  if (remove(path))
    fprintf(stderr, "remove failed!\n");
  if (rename(rp, path))
    fprintf(stderr, "rename failed!\n");
  if (symlink(path, rp))
    fprintf(stderr, "symlink failed!\n");
  free(rp);
}