/*
 * hlgroup.c - Supporting parameters for highlight groups
 *
 * This file is part of zsh, the Z shell.
 *
 * Copyright (c) 2024 Oliver Kiddle
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and to distribute modified versions of this software for any
 * purpose, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * In no event shall Oliver Kiddle or the Zsh Development Group be liable
 * to any party for direct, indirect, special, incidental, or consequential
 * damages arising out of the use of this software and its documentation,
 * even if Oliver Kiddle and the Zsh Development Group have been advised of
 * the possibility of such damage.
 *
 * Oliver Kiddle and the Zsh Development Group specifically disclaim any
 * warranties, including, but not limited to, the implied warranties of
 * merchantability and fitness for a particular purpose.  The software
 * provided hereunder is on an "as is" basis, and Oliver Kiddle and the
 * Zsh Development Group have no obligation to provide maintenance,
 * support, updates, enhancements, or modifications.
 *
 */

#include "hlgroup.mdh"
#include "hlgroup.pro"

#define GROUPVAR ".zle.hlgroups"

static const struct gsu_scalar pmesc_gsu =
{ strgetfn, nullstrsetfn, nullunsetfn };

/**/
static char *
convertattr(char *attrstr, int sgr)
{
    zattr atr;
    char *r, *s;
    int len;

    match_highlight(attrstr, &atr, NULL);
    s = zattrescape(atr, sgr ? NULL : &len);

    if (sgr) {
	char *c = s, *t = s - 1;

	while (c[0] == '\033' && c[1] == '[') {
	    c += 2;
	    while (isdigit(*c) || *c == ';')
		*++t = *c++;
	    t++;
	    if (*c != 'm')
		break;
	    *t = ';';
	    c++;
	}
	*t = '\0';
	len = t - s;
    }

    r = dupstring_wlen(s, len);
    free(s);
    return r;
}

/**/
static HashNode
getgroup(const char *name, int sgr)
{
    Param pm = NULL;
    HashNode hn;
    HashTable hlg;
    Value v;
    struct value vbuf;
    char *var = GROUPVAR;

    pm = (Param) hcalloc(sizeof(struct param));
    pm->gsu.s = &pmesc_gsu;
    pm->node.nam = dupstring(name);
    pm->node.flags = PM_SCALAR|PM_SPECIAL;

    if (!(v = getvalue(&vbuf, &var, 0)) ||
	     PM_TYPE(v->pm->node.flags) != PM_HASHED ||
	     !(hlg = v->pm->gsu.h->getfn(v->pm)) ||
	     !(hn = gethashnode2(hlg, name)))
    {
	pm->u.str = dupstring("");
	pm->node.flags |= PM_UNSET;
    } else {
	pm->u.str = convertattr(((Param) hn)->u.str, sgr);
    }

    return &pm->node;
}

/**/
static void
scangroup(ScanFunc func, int flags, int sgr)
{
    struct param pm;
    int i;
    HashNode hn;
    HashTable hlg;
    Value v;
    struct value vbuf;
    char *var = GROUPVAR;

    if (!(v = getvalue(&vbuf, &var, 0)) ||
	     PM_TYPE(v->pm->node.flags) != PM_HASHED)
	return;
    hlg = v->pm->gsu.h->getfn(v->pm);

    memset((void *)&pm, 0, sizeof(struct param));
    pm.node.flags = PM_SCALAR;
    pm.gsu.s = &pmesc_gsu;

    for (i = 0; i < hlg->hsize; i++)
	for (hn = hlg->nodes[i]; hn; hn = hn->next) {
	    pm.u.str = convertattr(((Param) hn)->u.str, sgr);
	    pm.node.nam = hn->nam;
	    func(&pm.node, flags);
	}
}
/**/
static HashNode
getpmesc(UNUSED(HashTable ht), const char *name)
{
    return getgroup(name, 0);
}

/**/
static void
scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    return scangroup(func, flags, 0);
}

/**/
static HashNode
getpmsgr(UNUSED(HashTable ht), const char *name)
{
    return getgroup(name, 1);
}

/**/
static void
scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags)
{
    return scangroup(func, flags, 1);
}

static struct paramdef partab[] = {
    SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc),
    SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr)
};

static struct features module_features = {
    NULL, 0,
    NULL, 0,
    NULL, 0,
    partab, sizeof(partab)/sizeof(*partab),
    0
};

/**/
int
setup_(UNUSED(Module m))
{
    return 0;
}

/**/
int
features_(Module m, char ***features)
{
    *features = featuresarray(m, &module_features);
    return 0;
}

/**/
int
enables_(Module m, int **enables)
{
    return handlefeatures(m, &module_features, enables);
}

/**/
int
boot_(UNUSED(Module m))
{
    return 0;
}

/**/
int
cleanup_(Module m)
{
    return setfeatureenables(m, &module_features, NULL);
}

/**/
int
finish_(UNUSED(Module m))
{
    return 0;
}
