/* Copyright (c) 2002
 *	Marko Boomstra (m.boomstra@chello.nl).  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 REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 */
#include <conf.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <ncurses.h>
#include <panel.h>
#include "mudix.h"


typedef enum
{
    OP_NONE,
    OP_EQUAL,           /* == */
    OP_INEQUAL,         /* != */
    OP_GREATER,         /* >  */
    OP_GREATER_EQUAL,   /* >= */
    OP_LESSER,          /* <  */
    OP_LESSER_EQUAL     /* <= */
} OPERATOR_TYPE;


void show_vars(void)
{
    static int		 current;
    	   VAR     	*var;
    	   char    	*str;
    	   int      	 line, i, width, height, centre, wsize;

    width  = LEN_COL-4;
    height = 20;
    wsize  = height-2;
    centre = ((width-2)/2)-1;

    wresize(wMsg, height, width);
    werase(wMsg);
    move_panel(pMsg, 1, 2);

    if (panel_hidden(pMsg) == PANEL_HIDDEN)
	current = 0;

    for (line=1, i=0, var = settings->vars_list; var; var = var->next) {
    	if (!var->name || !var->value)
            continue;

	i+=2;
	if (i < (current*wsize)+2)
	    continue;

      	mvwprintw(wMsg, line++, 1, "Name : %s", var->name);
      	mvwprintw(wMsg, line++, 1, "Value: %s", var->value); 

	if (line >= wsize && var->next) {
	    current++;
	    break;
	}

	if (!var->next)		/* last one */
	    current = 0;
    }

    draw_border(wMsg);
    str = "Variables";
    mvwprintw(wMsg, 0, centre-strlen(str)/2, str);
    show_panel(pMsg);
    return;
}

VAR *new_var(bool last)
{
    VAR *var;

    if (!(var = (VAR *)malloc(sizeof(VAR))))
        return NULL;

    if (last && settings->vars_list) {
        VAR *iVar;

        for (iVar = settings->vars_list; iVar; iVar = iVar->next) {
            if (!iVar->next)
                break;
        }
        iVar->next = var;
        var->next  = NULL;
    }
    else {
        var->next = settings->vars_list;
        settings->vars_list = var;
    }

    var->name  = NULL;
    var->value = NULL;

    return var;
}

void free_var(VAR *var)
{
    VAR *iVar;

    if (!var)
        return;

    for (iVar = settings->vars_list; iVar; iVar = iVar->next) {
        if (iVar->next == var) {
            iVar->next = var->next;
            break;
        }
    }

    if (var == settings->vars_list)
        settings->vars_list = var->next;

    if (var->name)
        free(var->name);
    if (var->value)
        free(var->value);
    free(var);
    return;
}

VAR *var_lookup(char *var)
{
    VAR *iVar;

    for (iVar = settings->vars_list; iVar; iVar = iVar->next) {
	if (!iVar->name || !iVar->value)
	    continue;

    	if (!strcmp(var, iVar->name))
	    break;
    }

    return iVar;
}


static bool is_numeric(char *str, double *value)
{
    char    *begin = str;

    /* parse till end of string */
    while (*str != '\0')
    {
        /* character in the string is not a digit! no numeric string */
        if (!isdigit(*str))
        {
            return FALSE;
        }
        str++;
    }
    
    *value = strtod(begin, NULL);

    return TRUE;
}


static OPERATOR_TYPE get_operator(char *expr)
{
    OPERATOR_TYPE retval = OP_NONE;

    switch (*expr)
    {
        case '!':   /* != */
            if (*(expr+1) == '=')
            {
                retval = OP_INEQUAL;
            }
            break;
        case '=':   /* == */
            if (*(expr+1) == '=')
            {
                retval = OP_EQUAL;
            }
            break;
        case '>':   /* >=, > */
            if (*(expr+1) == '=')
            {
                retval = OP_GREATER_EQUAL;
            }
            else
            {
                retval = OP_GREATER;
            }
            break;
        case '<':   /* <=, < */
            if (*(expr+1) == '=')
            {
                retval = OP_LESSER_EQUAL;
            }
            else
            {
                retval = OP_LESSER;
            }
            break;
        default:
            break;
    }
    
    return retval;
}


static bool check_expression(char *expr)
{
    static char             arg1[MAX_STRING];
    static char             arg2[MAX_STRING];
           VAR             *var;
           char            *pExpr = expr;
           char            *pArg1, *pArg2;
           OPERATOR_TYPE    opr;
           bool             retval, fNumeric = FALSE;
           double           lval, rval;

    /* get the first argument */
    pArg1 = arg1;
    while (*pExpr) 
    {
	if (*pExpr == '!' || 
            *pExpr == '=' ||
            *pExpr == '>' ||
            *pExpr == '<')
        {
	    break;
        }
	*pArg1++ = *pExpr++;
    }
    *pArg1 = '\0';

    /* retrieve the operator type */
    opr = get_operator(pExpr);
    if (opr == OP_NONE)
    {
        return FALSE;
    }
    else if (opr == OP_LESSER || opr == OP_GREATER)
    {
        pExpr++;
    }
    else
    {
        pExpr += 2;
    }

    /* copy the 2nd argument into arg2 */
    strcpy(arg2, pExpr);

    /* look up if this references a variable */
    var = var_lookup(arg1);
    if (var)
    {
        /* set the argument pointer to point to the variable value :) */
        pArg1 = var->value;
    }
    else
    {
        /* set to the original argument */
        pArg1 = arg1;
    }

    /* look up if this references a variable */
    var = var_lookup(arg2);
    if (var)
    {
        /* set the argument pointer to point to the variable value :) */
        pArg2 = var->value;
    }
    else
    {
        /* set to the original argument */
        pArg2 = arg2;
    }

    /* check if arguments are numeric */
    if (is_numeric(pArg1, &lval) && is_numeric(pArg2, &rval))
    {
        fNumeric = TRUE;
    }

    /* now check the operand type and the values */
    switch (opr)
    {
        case OP_GREATER:
            retval = fNumeric? (lval > rval):
                               (strcmp(pArg1, pArg2) > 0);
            break;
        case OP_GREATER_EQUAL:
            retval = fNumeric? (lval >= rval):
                               (strcmp(pArg1, pArg2) >= 0);
            break;
        case OP_LESSER:
            retval = fNumeric? (lval < rval):
                               (strcmp(pArg1, pArg2) < 0);
            break;
        case OP_LESSER_EQUAL:
            retval = fNumeric? (lval <= rval):
                               (strcmp(pArg1, pArg2) <= 0);
            break;
        case OP_INEQUAL:
            retval = fNumeric? (lval != rval):
                               (strcmp(pArg1, pArg2) != 0);
            break;
        case OP_EQUAL:
            retval = fNumeric? (lval == rval):
                               (strcmp(pArg1, pArg2) == 0);
            break;
        default:
            retval = FALSE;
            break;
    }

    return retval;
}


bool process_if(char *buffer)
{
    char str[MAX_STRING];
 
    /* grab the expression */
    buffer = get_arg(buffer, str);

    /* and check it */
    if (check_expression(str))
    {
        /* grab first argument after expression and process it */
        get_arg(buffer, str);
        process_input(str, NULL, TRUE);
	return TRUE;
    }
    else
    {
        /* skip to 2nd argument */
        buffer = get_arg(buffer, str);
        get_arg(buffer, str);

        if (str[0])
        {
            /* process the 'else' command */
            process_input(str, NULL, TRUE);
        }
        return FALSE;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1