/*
 * wslua_proto_expert.c
 *
 * Wireshark's interface to the Lua Programming Language
 *
 * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
 * (c) 2008, Balint Reczey <balint.reczey@ericsson.com>
 * (c) 2011, Stig Bjorlykke <stig@bjorlykke.org>
 * (c) 2014, Hadriel Kaplan <hadrielk@yahoo.com>
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"

#include "wslua.h"


/* WSLUA_CONTINUE_MODULE Proto */


WSLUA_CLASS_DEFINE(ProtoExpert,FAIL_ON_NULL("null ProtoExpert"));
    /* A Protocol expert info field, to be used when adding items to the dissection tree. */

WSLUA_CONSTRUCTOR ProtoExpert_new(lua_State* L) {
    /* Creates a new `ProtoExpert` object to be used for a protocol's expert information notices. */
#define WSLUA_ARG_ProtoExpert_new_ABBR 1 /* Filter name of the expert info field (the string that
                                            is used in filters). */
#define WSLUA_ARG_ProtoExpert_new_TEXT 2 /* The default text of the expert field. */
#define WSLUA_ARG_ProtoExpert_new_GROUP 3 /* Expert group type: one of:
                                             `expert.group.CHECKSUM`,
                                             `expert.group.SEQUENCE`,
                                             `expert.group.RESPONSE_CODE`,
                                             `expert.group.REQUEST_CODE`,
                                             `expert.group.UNDECODED`,
                                             `expert.group.REASSEMBLE`,
                                             `expert.group.MALFORMED`,
                                             `expert.group.DEBUG`,
                                             `expert.group.PROTOCOL`,
                                             `expert.group.SECURITY`,
                                             `expert.group.COMMENTS_GROUP`,
                                             `expert.group.DECRYPTION`,
                                             `expert.group.ASSUMPTION`,
                                             `expert.group.DEPRECATED`,
                                             `expert.group.RECEIVE`,
                                             or `expert.group.INTERFACE`. */
#define WSLUA_ARG_ProtoExpert_new_SEVERITY 4 /* Expert severity type: one of:
                                                `expert.severity.COMMENT`,
                                                `expert.severity.CHAT`,
                                                `expert.severity.NOTE`,
                                                `expert.severity.WARN`,
                                                or `expert.severity.ERROR`. */

    ProtoExpert pe    = NULL;
    const char* abbr = wslua_checkstring_only(L,WSLUA_ARG_ProtoExpert_new_ABBR);
    const char* text = wslua_checkstring_only(L,WSLUA_ARG_ProtoExpert_new_TEXT);
    int group         = (int)luaL_checkinteger(L, WSLUA_ARG_ProtoExpert_new_GROUP);
    int severity      = (int)luaL_checkinteger(L, WSLUA_ARG_ProtoExpert_new_SEVERITY);

    if (!abbr[0]) {
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "Empty field name abbrev");
        return 0;
    }

    if (proto_check_field_name(abbr)) {
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "Invalid char in abbrev");
        return 0;
    }

    if (proto_registrar_get_byname(abbr)) {
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_ABBR, "This abbrev already exists");
        return 0;
    }

    if (!text[0]) {
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_TEXT, "Empty text");
        return 0;
    }

    switch (group) {
    case PI_CHECKSUM:
    case PI_SEQUENCE:
    case PI_RESPONSE_CODE:
    case PI_REQUEST_CODE:
    case PI_UNDECODED:
    case PI_REASSEMBLE:
    case PI_MALFORMED:
    case PI_DEBUG:
    case PI_PROTOCOL:
    case PI_SECURITY:
    case PI_COMMENTS_GROUP:
    case PI_DECRYPTION:
    case PI_ASSUMPTION:
    case PI_DEPRECATED:
    case PI_RECEIVE:
    case PI_INTERFACE:
    case PI_DISSECTOR_BUG:
        break;
    default:
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_GROUP, "Group must be one of expert.group.*");
        return 0;
    }

    switch (severity) {
    case PI_COMMENT:
    case PI_CHAT:
    case PI_NOTE:
    case PI_WARN:
    case PI_ERROR:
        break;
    default:
        luaL_argerror(L, WSLUA_ARG_ProtoExpert_new_SEVERITY, "Severity must be one of expert.severity.*");
        return 0;
    }

    pe = g_new(wslua_expert_field_t,1);

    pe->ids.ei   = EI_INIT_EI;
    pe->ids.hf   = -2;
    pe->abbrev   = g_strdup(abbr);
    pe->text     = g_strdup(text);
    pe->group    = group;
    pe->severity = severity;

    pushProtoExpert(L,pe);

    WSLUA_RETURN(1); /* The newly created `ProtoExpert` object. */
}

/* WSLUA_ATTRIBUTE ProtoExpert_abbrev RO The filter name/abbreviation of the
   expert field. */
WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(ProtoExpert,abbrev,abbrev);

/* WSLUA_ATTRIBUTE ProtoExpert_text RO The default text of the expert field. */
WSLUA_ATTRIBUTE_NAMED_STRING_GETTER(ProtoExpert,text,text);

/* WSLUA_ATTRIBUTE ProtoExpert_group RO The expert group as a descriptive
   string (matches expert.group.*). */
WSLUA_ATTRIBUTE_GET(ProtoExpert,group, {
    lua_pushstring(L, val_to_str_const(obj->group, expert_group_vals,
                                       "Unknown"));
});

/* WSLUA_ATTRIBUTE ProtoExpert_severity RO The severity level as a
   descriptive string (matches expert.severity.*). */
WSLUA_ATTRIBUTE_GET(ProtoExpert,severity, {
    lua_pushstring(L, val_to_str_const(obj->severity, expert_severity_vals,
                                       "Unknown"));
});

WSLUA_METAMETHOD ProtoExpert__tostring(lua_State* L) {
    /* Returns a short label of the form
       `ProtoExpert: <abbrev> severity=<severity> text="<text>"`
       where <severity> is the human-readable severity name
       (matching `ProtoExpert.severity`). Replaces the previous
       integer-only dump of every internal id, which is still
       reachable via the individual `abbrev`/`text`/`group`/
       `severity` attributes. */
    ProtoExpert pe = toProtoExpert(L,1);

    if (!pe) {
        lua_pushstring(L, "ProtoExpert: (null)");
    } else {
        const char *sev = val_to_str_const(pe->severity, expert_severity_vals,
                                           "Unknown");
        lua_pushfstring(L, "ProtoExpert: %s severity=%s text=\"%s\"",
                        pe->abbrev ? pe->abbrev : "?",
                        sev,
                        pe->text ? pe->text : "");
    }
    return 1;
}

static int ProtoExpert__gc(lua_State* L) {
    ProtoExpert pe = toProtoExpert(L,1);

    /*
     * Initialized to -2 in ProtoExpert_new,
     * changed to -1 in Proto_commit and subsequently replaced by
     * an allocated number in proto_register_field_array.
     * Reset to -2 again in wslua_deregister_protocols.
     */
    if (pe->ids.hf != -2) {
        /* Only free unregistered and deregistered ProtoExpert */
        return 0;
    }

    g_free((char *)pe->abbrev);
    g_free((char *)pe->text);
    g_free(pe);

    return 0;
}

WSLUA_METHODS ProtoExpert_methods[] = {
    WSLUA_CLASS_FNREG(ProtoExpert,new),
    { NULL, NULL }
};

WSLUA_META ProtoExpert_meta[] = {
    WSLUA_CLASS_MTREG(ProtoExpert,tostring),
    { NULL, NULL }
};

/* Registered as a sub-table of the class' metatable so that
 * ProtoExpert.abbrev, .text, .group and .severity resolve via the wslua
 * attribute dispatcher. This also lets the Lua debugger drill down into
 * each ProtoExpert in Proto.experts. */
WSLUA_ATTRIBUTES ProtoExpert_attributes[] = {
    WSLUA_ATTRIBUTE_ROREG(ProtoExpert,abbrev),
    WSLUA_ATTRIBUTE_ROREG(ProtoExpert,text),
    WSLUA_ATTRIBUTE_ROREG(ProtoExpert,group),
    WSLUA_ATTRIBUTE_ROREG(ProtoExpert,severity),
    { NULL, NULL, NULL }
};

int ProtoExpert_register(lua_State* L) {
    WSLUA_REGISTER_CLASS_WITH_ATTRS(ProtoExpert);
    return 0;
}


/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=8 expandtab:
 * :indentSize=4:tabSize=8:noTabs=true:
 */
