/*
 * vim:tw=80:expandtab
 *
 * Copyright (c) 2014 The MacPorts Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <stdlib.h>
#include <tcl.h>
#include <sqlite3.h>

#include "portgroupobj.h"
#include "registry.h"
#include "util.h"

const char* portgroup_props[] = {
    "name",
    "version",
    "size",
    "sha256",
    NULL
};

/* ${portgroup} prop ?value? */
static int portgroup_obj_prop(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
        Tcl_Obj* const objv[]) {
    int index;
    if (objc > 3) {
        Tcl_WrongNumArgs(interp, 2, objv, "?value?");
        return TCL_ERROR;
    }
    if (objc == 2) {
        /* ${portgroup} prop; return the current value */
        reg_registry* reg = registry_for(interp, reg_attached);
        if (reg == NULL) {
            return TCL_ERROR;
        }
        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, "prop", 0, &index)
                == TCL_OK) {
            char* key = Tcl_GetString(objv[1]);
            char* value;
            reg_error error;
            if (reg_portgroup_propget(portgroup, key, &value, &error)) {
                Tcl_Obj* result = Tcl_NewStringObj(value, -1);
                Tcl_SetObjResult(interp, result);
                free(value);
                return TCL_OK;
            }
            return registry_failed(interp, &error);
        }
        return TCL_ERROR;
    } else {
        /* ${portgroup} prop name value; set a new value */
        reg_registry* reg = registry_for(interp, reg_attached);
        if (reg == NULL) {
            return TCL_ERROR;
        }
        if (Tcl_GetIndexFromObj(interp, objv[1], portgroup_props, "prop", 0, &index)
                == TCL_OK) {
            char* key = Tcl_GetString(objv[1]);
            char* value = Tcl_GetString(objv[2]);
            reg_error error;
            if (reg_portgroup_propset(portgroup, key, value, &error)) {
                return TCL_OK;
            }
            return registry_failed(interp, &error);
        }
        return TCL_ERROR;
    }
}

typedef struct {
    char* name;
    int (*function)(Tcl_Interp* interp, reg_portgroup* portgroup, int objc,
            Tcl_Obj* const objv[]);
} portgroup_obj_cmd_type;

static portgroup_obj_cmd_type portgroup_cmds[] = {
    /* keys */
    { "name", portgroup_obj_prop },
    { "version", portgroup_obj_prop },
    { "size", portgroup_obj_prop },
    { "sha256", portgroup_obj_prop },
    { NULL, NULL }
};

/* ${portgroup} cmd ?arg ...? */
/* This function implements the command that will be called when a portgroup
 * created by `registry::portgroup` is used as a procedure. Since all data is kept
 * in a temporary sqlite3 database that is created for the current interpreter,
 * none of the sqlite3 functions used have any error checking. That should be a
 * safe assumption, since nothing outside of registry:: should ever have the
 * chance to touch it.
 */
int portgroup_obj_cmd(ClientData clientData, Tcl_Interp* interp, int objc,
        Tcl_Obj* const objv[]) {
    int cmd_index;
    if (objc < 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?");
        return TCL_ERROR;
    }
    if (Tcl_GetIndexFromObjStruct(interp, objv[1], portgroup_cmds,
                sizeof(portgroup_obj_cmd_type), "cmd", 0, &cmd_index) == TCL_OK) {
        portgroup_obj_cmd_type* cmd = &portgroup_cmds[cmd_index];
        return cmd->function(interp, (reg_portgroup*)clientData, objc, objv);
    }
    return TCL_ERROR;
}

