/*
   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include <atalk/cnid.h>
#include <atalk/logger.h>
#include <atalk/netatalk_conf.h>
#include <atalk/util.h>

#include "nad.h"

/* Workaround for mismatched gid_t type and getgrouplist() prototype on macOS */
#ifdef __APPLE__
#define my_gid_type int
#else
#define my_gid_type gid_t
#endif

typedef enum {
    CMD_UNKNOWN = -1,
    CMD_LS,
    CMD_CP,
    CMD_RM,
    CMD_MV,
    CMD_SET,
    CMD_FIND,
    CMD_MKDIR,
    CMD_RMDIR,
    CMD_VERSION
} nad_command_t;

static nad_command_t get_command(const char *cmd)
{
    if (STRCMP(cmd, ==, "ls")) {
        return CMD_LS;
    }

    if (STRCMP(cmd, ==, "cp")) {
        return CMD_CP;
    }

    if (STRCMP(cmd, ==, "rm")) {
        return CMD_RM;
    }

    if (STRCMP(cmd, ==, "mv")) {
        return CMD_MV;
    }

    if (STRCMP(cmd, ==, "set")) {
        return CMD_SET;
    }

    if (STRCMP(cmd, ==, "find")) {
        return CMD_FIND;
    }

    if (STRCMP(cmd, ==, "mkdir")) {
        return CMD_MKDIR;
    }

    if (STRCMP(cmd, ==, "rmdir")) {
        return CMD_RMDIR;
    }

    if (STRCMP(cmd, ==, "-v") || STRCMP(cmd, ==, "--version")) {
        return CMD_VERSION;
    }

    return CMD_UNKNOWN;
}

static void usage_main(void)
{
    printf("Usage: nad [-F configfile] ls|cp|rm|mv|mkdir|rmdir|set|find [file|dir, ...]\n");
    printf("       nad -v|--version\n");
}

static void show_version(void)
{
    printf("nad (Netatalk %s)\n", VERSION);
}

int main(int argc, char **argv)
{
    AFPObj obj = { 0 };
    int arg_idx = 1;

    if (argc < 2) {
        usage_main();
        return 1;
    }

    while (arg_idx < argc) {
        if (strcmp(argv[arg_idx], "-F") == 0) {
            if (++arg_idx >= argc) {
                usage_main();
                return 1;
            }

            obj.cmdlineconfigfile = argv[arg_idx++];
        } else {
            break;
        }
    }

    if (afp_config_parse(&obj, "nad") != 0) {
        return 1;
    }

    obj.uid = getuid();
    const struct passwd *pwd = getpwuid(obj.uid);

    if (pwd) {
        int ngroups = 0;
        my_gid_type *groups = NULL;
        getgrouplist(pwd->pw_name, pwd->pw_gid, NULL, &ngroups);
        groups = malloc(sizeof(my_gid_type) * ngroups);

        if (groups) {
            if (getgrouplist(pwd->pw_name, pwd->pw_gid, groups, &ngroups) >= 0) {
                obj.groups = malloc(sizeof(my_gid_type) * ngroups);

                if (obj.groups) {
                    memcpy(obj.groups, groups, sizeof(my_gid_type) * ngroups);
                    obj.ngroups = ngroups;
                }
            }

            free(groups);
        }
    }

    setuplog(obj.options.logconfig, obj.options.logfile,
             obj.options.log_us_timestamp);

    if (load_volumes(&obj, LV_DEFAULT) != 0) {
        return 1;
    }

    if (arg_idx >= argc) {
        usage_main();
        return 1;
    }

    nad_command_t cmd = get_command(argv[arg_idx]);

    switch (cmd) {
    case CMD_LS:
        return nad_ls(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_CP:
        return nad_cp(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_RM:
        return nad_rm(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_MV:
        return nad_mv(argc - arg_idx + 1, argv + arg_idx - 1, &obj);

    case CMD_SET:
        return nad_set(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_FIND:
        return nad_find(argc - arg_idx + 1, argv + arg_idx - 1, &obj);

    case CMD_MKDIR:
        return nad_mkdir(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_RMDIR:
        return nad_rmdir(argc - arg_idx, argv + arg_idx, &obj);

    case CMD_VERSION:
        show_version();
        return 1;

    case CMD_UNKNOWN:
    default:
        usage_main();
        return 1;
    }
}
