/*
 * hsclib/defattr.c
 *
 * functions to define new attribute
 * and manipulate attribute lists
 *
 * Copyright (C) 1995,96  Thomas Aglassinger
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * updated: 13-Oct-1996
 * created:  6-Jan-1995
 */

#define NOEXTERN_HSCLIB_DEFATTR

#include "hsclib/inc_base.h"

#include "hsclib/defattr.h"
#include "hsclib/eval.h"
#include "hsclib/input.h"

/*
 *-------------------------------------
 * misc. functions
 *-------------------------------------
 */
static BYTE str2vartype(STRPTR s)
{
    BYTE vartype = VT_NONE;

    if (!upstrcmp(VT_STR_URI, s))
        vartype = VT_URI;
    else if (!upstrcmp(VT_STR_STRING, s))
        vartype = VT_STRING;
    else if (!upstrcmp(VT_STR_BOOL, s))
        vartype = VT_BOOL;
    else if (!upstrcmp(VT_STR_NUM, s))
        vartype = VT_NUM;
    else if (!upstrcmp(VT_STR_ENUM, s))
        vartype = VT_ENUM;
    else if (!upstrcmp(VT_STR_ID, s))
        vartype = VT_ID;
    else if (!upstrcmp(VT_STR_COLOR, s))
        vartype = VT_COLOR;

    return (vartype);
}

/*
 * check_varlist: check for required attributes missing
 *
 * result: TRUE, if all attributes ok
 */
static BOOL check_reqvar(HSCPRC * hp, HSCATTR * var)
{
    BOOL ok = TRUE;

    if ((var->varflag & VF_REQUIRED)
        && (!var->text))
    {
        hsc_message(hp, MSG_MISS_REQ_ATTR,
                    "required %A missing", var);
        ok = FALSE;
    }

    return (ok);
}

BOOL check_varlist(HSCPRC * hp, DLLIST * varlist)
{
    DLNODE *nd = varlist->first;
    BOOL ok = TRUE;

    while (nd)
    {
        ok &= check_reqvar(hp, (HSCATTR *) (nd->data));
        nd = nd->next;
    }

    return (ok);
}

/*
 *-------------------------------------
 * define a new var from input file
 *-------------------------------------
 */

/*
 * read_enum_str
 *
 * sidefx: modifies tmpstr
 */
static BOOL read_enum_str(HSCPRC * hp, HSCATTR * var)
{
    HSCATTR *attr = new_hscattr(PREFIX_TMPATTR "enumerator");
    attr->vartype = VT_STRING;

    /* store enumstr in var-struct */
    if (eval_expression(hp, attr, NULL))
    {
        DDA(fprintf(stderr, DHL "  enum: %s\n", estr2str(hp->tmpstr)));
        var->enumstr = strclone(get_vartext(attr));
    }

    del_hscattr(attr);

    return ((BOOL) (!hp->fatal));
}

/*
 * check_attr_option
 *
 * check if a attribute-option-string is equal to an id/short id.
 * if so, set the corresponding option value within the attribute.
 *
 * params: option..option string to check for (read from input)
 *         attr....attribute to update option value for
 *         id......id string of option (eg "REQUIRED")
 *         sid.....short id string (eg "R")
 *         value...option value to OR with old tag's option value
 * result: TRUE, if tag's option value updated
 */
static BOOL check_attr_option(HSCPRC * hp, STRPTR option, HSCATTR * attr, STRPTR id, STRPTR sid, ULONG value, ULONG unmasked_flags)
{
    BOOL found = FALSE;

    if (!((upstrcmp(option, id)) && (upstrcmp(option, sid))))
    {
        DDA(fprintf(stderr, DHL "  option %s\n", id));

        if (value & unmasked_flags)
        {
            hsc_message(hp, MSG_ILLG_ATTR_FLAG,
                        "attribute option %q not allowed in this context",
                        id);
        }
        else
            attr->varflag |= value;

        found = TRUE;
    }

    return (found);
}

/*
 * define_var
 *
 * define a new var with reading its def from input file
 * (starts parsing after ":", so ":" has to be read before)
 *
 * params: varname..name of new var
 *         varlist..list new var should be inserted at the beginning
 *         inpf.....input file where to read def from
 *         flag.....flags: VF_ONLYONCE to avoid re-definition of a var
 * result: ptr to new var
 *
 * definition syntax in input file:
 *   <vartype>[/flag]["="<deftext value>]
 *   legal vartypes: see VT_STR_xx in "vars.h"
 *   legal flags   : see VF_STR_xx in "vars.h"
 */
HSCATTR *define_var(HSCPRC * hp, DLLIST * varlist, ULONG unmasked_flags)
{
    HSCATTR *var = NULL;        /* result */
    BOOL ok = FALSE;
    BYTE val_vartype = VT_NONE; /* var-type (numeric) */
    BOOL newattr = FALSE;       /* next word read from input */
    STRPTR nw = NULL;
    STRPTR varname = NULL;
    BOOL eof_called = FALSE;    /* used at end-of-func, if nw==NULL */
    INFILE *inpf = hp->inpf;

    /* read attribute name */
    nw = infget_attrid(hp);
    if (nw)
    {
        varname = strclone(nw); /* remember attribute name */
    }
    else
        eof_called = TRUE;      /* err_eof() called already */

    /* read attribute type */
    if (nw)
        if (parse_wd(hp, ":"))
        {
            nw = infgetw(inpf);
            if (nw)
                val_vartype = str2vartype(nw);
        }
        else
            inungetcw(inpf);

    if (nw)
    {
        /*
         * look if attr already exist;
         * if yes, clear old attribute
         * to redefine the new one
         */
        var = find_varname(varlist, varname);
        if (var)
        {
            DLNODE *nd = find_attrnode(varlist, varname);

            /* remove old attribute */
            if (nd)
                del_dlnode(varlist, nd);
            else
                panic("no node for redefined attribute");

            hsc_message(hp, MSG_ATTR_REDEFINED,
                        "redefined %a", varname);
        }

        /*
         * create new attribute
         */
        DDA(fprintf(stderr, DHL "new attr: %s\n", varname));
        var = app_var(varlist, varname);

        /* set type */
        var->vartype = val_vartype;
        if (var->vartype == VT_ENUM)
        {
            /* init enum-attribute */
            read_enum_str(hp, var);
        }
        else if (var->vartype == VT_BOOL)
        {
            /* init boolean attr with FALSE */
            set_varbool(var, FALSE);
        }

        newattr = TRUE;

    }

    /* disable "/STRIPEXT" and "/GETSIZE" for non-URI-attributes */
    if (nw)
    {
        if (var->vartype != VT_URI)
            unmasked_flags |= VF_GETSIZE | VF_STRIPEXT;

        nw = infgetw(inpf);     /* get net word */
    }

    /*
     * handle attribute flags
     */
    while (nw && !strcmp(nw, "/"))
    {
        nw = infgetw(inpf);     /* read flag identifier */
        if (nw)
        {
            BOOL ok = FALSE;

            ok |= check_attr_option(hp, nw, var,
                                    VF_CONST_STR, VF_CONST_SHT,
                                    VF_CONST, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_GLOBAL_STR, VF_GLOBAL_SHT,
                                    VF_GLOBAL, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_JERK_STR, VF_JERK_SHT,
                                    VF_JERK, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_ONLYONCE_STR, VF_ONLYONCE_SHT,
                                    VF_ONLYONCE, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_REQUIRED_STR, VF_REQUIRED_SHT,
                                    VF_REQUIRED, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_GETSIZE_STR, VF_GETSIZE_SHT,
                                    VF_GETSIZE, unmasked_flags);
            ok |= check_attr_option(hp, nw, var,
                                    VF_STRIPEXT_STR, VF_STRIPEXT_SHT,
                                    VF_STRIPEXT, unmasked_flags);
            if (!ok)
            {
                hsc_message(hp, MSG_UNKN_ATTR_OPTION,
                            "unknown attribute flag %q", nw);
            }

            /* read next word (should be "/", "=" or next attr / ">") */
            nw = infgetw(inpf);
        }
        else
            hsc_msg_eof(hp, "defining attribute");

    }

    /*
     * handle default value
     */
    if (nw && !strcmp(nw, "="))
    {
        /* get new deftext value */
        STRPTR new_deftext = NULL;
        LONG old_attrflag = var->varflag;

        /* disable quotemode-checking */
        var->varflag |= VF_KEEP_QUOTES;

        if (!(var->deftext))
            new_deftext = eval_expression(hp, var, NULL);
        else
        {
            STRPTR dummy;

            hsc_message(hp, MSG_SYMB_2ND_DEFAULT,
                        "default value for %A already set", var);

            /* skip illegal default value */
            dummy = eval_expression(hp, var, NULL);
        }

        /* restore quotemode-checking */
        var->varflag = old_attrflag;

        /* store default text value */
        if (new_deftext)
            var->deftext = strclone(new_deftext);

        /* read next word, only to be ungotten below */
        nw = infgetw(inpf);
    }

    /* check for unexpected end of file */
    if (!nw)
    {
        if (!eof_called)
            hsc_msg_eof(hp, "defining attribute");
    }
    else
    {
        /* end of var definition reached */
        inungetcw(inpf);
        ok = TRUE;
    }

    /* cleanup */
    if (!ok && var)
    {
        DLNODE *nd = find_attrnode(varlist,varname);
        if (nd)
            del_dlnode(varlist, (APTR) nd);
        else
            del_hscattr(var);
        var = NULL;
    }
    ufreestr(varname);

    return (var);
}

/*
 *-------------------------------------
 * copy & remove local vars to/from
 * global varlist
 *-------------------------------------
 */

/*
 * copy_local_var
 *
 * copies a local attribute to the global attribute list
 *
 * NOTE: the VF_MACRO-flag of the copy is disabled!
 */
static HSCATTR *copy_local_var(DLLIST * destlist, HSCATTR * locvar, ULONG mci)
{
    HSCATTR *var = app_var(destlist, locvar->name);

    var->macro_id = mci;
    var->vartype = locvar->vartype;
    var->varflag = locvar->varflag & (~VF_MACRO);       /* disable VF_MACRO */
    set_vartext(var, locvar->text);
    var->quote = locvar->quote;

    return (var);
}

/*
 * copy_local_vars
 *
 * add all local attributes of a macro to the global
 * attribute list.
 *
 */
BOOL copy_local_varlist(DLLIST * destlist, DLLIST * varlist, ULONG mci)
{
    BOOL ok = TRUE;

    if (mci == MCI_ERROR)
        panic("mci=MCI_ERROR");
    else
    {
        DLNODE *nd = varlist->first;
        HSCATTR *var;

        while (nd && ok)
        {
            var = copy_local_var(destlist, (HSCATTR *) (nd->data), mci);
            ok &= (BOOL) (var != NULL);
            nd = nd->next;
        }
    }

    return (ok);
}

/*
 * set_local_var
 *
 * copies a local attribute to the global attribute list
 *
 * NOTE: the VF_MACRO-flag of the set is enabled!
 */
static HSCATTR *set_local_var(DLLIST * destlist, HSCATTR * locvar, ULONG mci)
{
    HSCATTR *var = find_varname(destlist, locvar->name);

    if (var)
    {
        var->macro_id = mci;
        var->vartype = locvar->vartype;
        set_vartext(var, locvar->text);
    }
    else
        panic("set_local_var to UNKNOWN ATTR");

    return (var);
}

/*
 * set_local_vars
 *
 * add all local attributes of a macro to the global
 * attribute list.
 *
 */
BOOL set_local_varlist(DLLIST * destlist, DLLIST * varlist, ULONG mci)
{
    BOOL ok = TRUE;

    if (mci == MCI_ERROR)
        panic("mci=MCI_ERROR");
    else
    {
        DLNODE *nd = varlist->first;
        HSCATTR *var;

        while (nd && ok)
        {
            var = set_local_var(destlist, (HSCATTR *) (nd->data), mci);
            ok &= (BOOL) (var != NULL);
            nd = nd->next;
        }
    }

    return (ok);
}

/*
 * remove_local_varlist
 */
VOID remove_local_varlist(DLLIST * varlist, ULONG mci)
{
    DLNODE *nd = varlist->first;

    while (nd)
    {
        HSCATTR *var = (HSCATTR *) nd->data;    /* var data of node */
        DLNODE *nd_nxt = nd->next;      /* next node */

        if (var->macro_id == mci)
            del_dlnode(varlist, nd);

        nd = nd_nxt;
    }
}

