#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
#include <Cocoa/Cocoa.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <libgen.h>
#include "macos.h"


#define MACOS_BASE_CONFIG       "tuxpaint.cfg"
#define MACOS_TEMP_CONFIG_TFMT  "%s/tuxpaint.cfg.XXXX"
#define MACOS_USER_CONFIG_PFMT  "%s/Library/Application Support/TuxPaint/tuxpaint.cfg"
#define MACOS_GLOBAL_CONFIG     "/Library/Application Support/TuxPaint/tuxpaint.cfg"

static const char* error_text = "OK";


const char* macos_filename(int config_t)
{
    static char user_config[PATH_MAX];
    static char temp_config_tfmt[PATH_MAX];
    const char* filename = MACOS_BASE_CONFIG;

    switch(config_t) {
        case MACOS_TEMP_CONFIG_T: {
            const char* tmpdir = getenv("TMPDIR");

            if(!temp_config_tfmt[0] && tmpdir) {
                snprintf(temp_config_tfmt, PATH_MAX, MACOS_TEMP_CONFIG_TFMT, tmpdir);
            }

            filename = mktemp(temp_config_tfmt);
            break;
        }

        case MACOS_USER_CONFIG_T: {
            const char* home = getenv("HOME");

            if(!user_config[0] && home) {
                snprintf(user_config, PATH_MAX, MACOS_USER_CONFIG_PFMT, home);
            }

            filename = user_config;
            break;
        }

        case MACOS_GLOBAL_CONFIG_T:
            filename = MACOS_GLOBAL_CONFIG;
            break;
    }

    return filename;
}


const char* macos_dirname(const char* filename)
{
    return dirname(filename);
}


const char* macos_error()
{
    return error_text;
}


int macos_mkdir(const char* dirname)
{
    #define COMMAND_MAX (PATH_MAX + 64)  /* directory name length + some padding for the string "mkdir -p" */
    char command[COMMAND_MAX];

    /*
    * We should use mkdir(2) but this is easier for auto-creating parent
    * directories.
    */
    snprintf(command, COMMAND_MAX, "mkdir -p '%s'", dirname);

    return system(command);
}


int macos_unlink(const char* filename)
{
    return unlink(filename);
}


int macos_sudo_cp(const char* source, const char* target)
{
    AuthorizationFlags flags = kAuthorizationFlagDefaults;
    AuthorizationEnvironment env = { 0, NULL };
    AuthorizationRef auth;
    OSStatus status = 0;
    int exitcode = 0;

    /* sudo */
    status = AuthorizationCreate(NULL, &env, flags, &auth);

    /* copy rights */
    if(status == errAuthorizationSuccess) {
        AuthorizationRights rights;
        AuthorizationItem items = { kAuthorizationRightExecute, 0, NULL, 0 };
        AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed
                                 | kAuthorizationFlagPreAuthorize
                                 | kAuthorizationFlagExtendRights;

        rights.count = 1;
        rights.items = &items;

        status = AuthorizationCopyRights(auth, &rights, &env, flags, NULL);
    }

    if(status == errAuthorizationSuccess) {
        const char* mkdir_args[] = { "-p"  , dirname(target), NULL };
        const char* cp_args[]    = { source, target         , NULL };
        FILE* pipe = NULL;

        /* mkdir && cp */
        AuthorizationExecuteWithPrivileges(auth, "/bin/mkdir", flags, mkdir_args, &pipe);
        wait(&exitcode);

        AuthorizationExecuteWithPrivileges(auth, "/bin/cp", flags, cp_args, &pipe);
        wait(&exitcode);

        /* sudo done! */
        AuthorizationFree(auth, flags);
    }

    if(status != errAuthorizationSuccess) error_text = "Authorization failure";
    if(exitcode != 0) {
        static char buf[256];

        sprintf(buf, "cp returned exit code: %d", exitcode);
        error_text = buf;
    }

    return (status == errAuthorizationSuccess) && (exitcode == 0);
}


/*
* Change the current working directory to the app bundle directory.
*/
int macos_appdir()
{
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];

    return [fileManager changeCurrentDirectoryPath: bundlePath];
}
