// SPDX-FileCopyrightText: 2007-2016 The original vcontrold authors (cf. doc/original_authors.txt)
//
// SPDX-License-Identifier: GPL-3.0-or-later

// Calculation of arithmetic expressions

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define HEX 8
#define HEXDIGIT 10
#define DIGIT 11
#define PUNKT 12
#define END 0
#define ERROR -100
#define PLUS 100
#define MINUS 101
#define MAL 102
#define GETEILT 103
#define MODULO 104
#define KAUF 110
#define KZU 111
#define BYTE0 200
#define BYTE1 201
#define BYTE2 202
#define BYTE3 203
#define BYTE4 204
#define BYTE5 205
#define BYTE6 206
#define BYTE7 207
#define BYTE8 208
#define BYTE9 209
#define BYTEA 210
#define BYTEB 211
#define BYTEC 212
#define BYTED 213
#define BYTEE 214
#define BYTEF 215
#define PBYTE0 216
#define PBYTE1 217
#define PBYTE2 218
#define PBYTE3 219
#define PBYTE4 220
#define PBYTE5 221
#define PBYTE6 222
#define PBYTE7 223
#define PBYTE8 224
#define PBYTE9 225
#define PBYTEA 226
#define PBYTEB 227
#define PBYTEC 228
#define PBYTED 229
#define PBYTEE 230
#define PBYTEF 231
#define BITPOS 232
#define VALUE 300
#define NICHT 400
#define UND 401
#define ODER 402
#define XOR 403
#define SHL 404
#define SHR 405

int nextToken(char **str, char **c, int *count);
void  pushBack(char **str, int n);
float execExpression(char **str, unsigned char *bPtr, float floatV, char *err);
float execTerm(char **str, unsigned char *bPtr, float floatV, char *err);
float execFactor(char **str, unsigned  char *bPtr, float floatV, char *err);
int execIExpression(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);
int execITerm(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);
int execIFactor(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err);

float execExpression(char **str, unsigned char *bInPtr, float floatV, char *err)
{
    int f = 1;
    float term1, term2;
    //float exp1,exp2;
    char *item;
    //unsigned char bPtr[10];
    unsigned char bPtr[16];
    int n;

    //printf("execExpression: %s\n",*str);

    // Tweak bPtr Bytes 0..9 and copy them to nPtr
    // We did not receive characters
    for (n = 0; n <= 15; n++) {
        //bPtr[n]=*bInPtr++ & 255;
        bPtr[n] = *bInPtr++;
    }

    switch (nextToken(str, &item, &n)) {
    case PLUS:
        f = 1;
        break;
    case MINUS:
        f = -1;
        break;
    default:
        pushBack(str, n);
        break;
    }

    term1 = execTerm(str, bPtr, floatV, err) * f;
    if (*err) {
        return 0;
    }
    //printf(" T1=%f\n",term1);

    int t;
    while ((t = nextToken(str, &item, &n)) != END) {
        f = 1;
        switch (t) {
        case PLUS:
            f = 1;
            break;
        case MINUS:
            f = -1;
            break;
        default:
            //printf(" Exp=%f\n",term1);
            pushBack(str, n);
            return term1;
        }

        term2 = execTerm(str, bPtr, floatV, err);
        if (*err) {
            return 0;
        }
        //printf(" T2=%f\n",term2);
        term1 += term2 * f;
    }

    //printf(" Exp=%f\n",term1);
    return term1;
}

float execTerm(char **str, unsigned char *bPtr, float floatV, char *err)
{
    float factor1, factor2;
    int op;
    char *item;
    int n;

    //printf("execTerm: %s\n",*str);

    factor1 = execFactor(str, bPtr, floatV, err);
    if (*err) {
        return 0;
    }

    //printf(" F1=%f\n",factor1);
    while (1) {
        switch (nextToken(str, &item, &n)) {
        case MAL:
            op = MAL;
            break;
        case GETEILT:
            op = GETEILT;
            break;
        default:
            pushBack(str, n);
            //printf("  ret(%f)\n",factor1);
            return factor1;
        }
        factor2 = execFactor(str, bPtr, floatV, err);
        //printf(" F2=%f\n",factor2);
        if (*err) {
            return 0;
        }
        if (op == MAL) {
            factor1 *= factor2;
        } else {
            factor1 /= factor2;
        }
    }
}

float execFactor(char **str, unsigned char *bPtr, float floatV, char *err)
{
    char nstring[100];
    float expression;
    float factor;
    char *nPtr;
    char *item;
    char token;
    int n;

    //printf("execFactor: %s\n",*str);

    switch (nextToken(str, &item, &n)) {
    case BYTE0:
        return bPtr[0];
    case BYTE1:
        return bPtr[1];
    case BYTE2:
        return bPtr[2];
    case BYTE3:
        return bPtr[3];
    case BYTE4:
        return bPtr[4];
    case BYTE5:
        return bPtr[5];
    case BYTE6:
        return bPtr[6];
    case BYTE7:
        return bPtr[7];
    case BYTE8:
        return bPtr[8];
    case BYTE9:
        return bPtr[9];
    case BYTEA:
        return bPtr[10];
    case BYTEB:
        return bPtr[11];
    case BYTEC:
        return bPtr[12];
    case BYTED:
        return bPtr[13];
    case BYTEE:
        return bPtr[14];
    case BYTEF:
        return bPtr[15];
    case VALUE:
        return floatV;
    case HEX:
        nPtr = nstring;
        memset(nstring, 0, sizeof(nstring));
        strcpy(nstring, "0x");
        nPtr += 2;
        token = nextToken(str, &item, &n);
        while ((token == DIGIT) || (token == HEXDIGIT)) {
            *nPtr++ = *item;
            token = nextToken(str, &item, &n);
        }
        pushBack(str, n);
        sscanf(nstring, "%f", &factor);
        return factor;
    case DIGIT:
        nPtr = nstring;
        do {
            *nPtr++ = *item;
        } while ((token = nextToken(str, &item, &n)) == DIGIT);
        // If a . follows, we have a decimal number
        if (token == PUNKT) {
            do {
                *nPtr++ = *item;
            } while ((token = nextToken(str, &item, &n)) == DIGIT);
        }
        pushBack(str, n);
        *nPtr = '\0';
        factor = atof(nstring);
        //printf("  Zahl: %s (f:%f)\n",nstring,factor);
        return factor;
    case KAUF:
        expression = execExpression(str, bPtr, floatV, err);
        if (*err) {
            return 0;
        }
        if (nextToken(str, &item, &n) != KZU) {
            sprintf(err, "expected factor:) [%c]\n", *item);
            return 0;
        }
        return expression;
    default:
        sprintf(err, "expected factor: B0..BF number ( ) [%c]\n", *item);
        return 0;
    }
}

int execIExpression(char **str, unsigned char *bInPtr, char bitpos, char *pPtr, char *err)
{
    int f = 1;
    int term1, term2;
    //int exp1, exp2;
    int op;
    char *item;
    unsigned char bPtr[16];
    int n;

    //printf("execExpression: %s\n", *str);

    // Tweak bPtr bytes 0..9 and copy them to nPtr
    // We have received characters
    for (n = 0; n <= 15; n++) {
        //bPtr[n]=*bInPtr++ & 255;
        bPtr[n] = *bInPtr++;
    }

    op = ERROR;
    switch (nextToken(str, &item, &n)) {
    case PLUS:
        op = PLUS;
        break;
    case MINUS:
        op = MINUS;
        break;
    case NICHT:
        op = NICHT;
        break;
    default:
        pushBack(str, n);
        break;
    }

    if (op == MINUS) {
        term1 = execITerm(str, bPtr, bitpos, pPtr, err) * -1;
    } else if (op == NICHT) {
        term1 = ~(execITerm(str, bPtr, bitpos, pPtr, err));
    } else {
        term1 = execITerm(str, bPtr, bitpos, pPtr, err);
    }

    if (*err) {
        return 0;
    }
    //printf(" T1=%d\n",term1);

    int t;
    op = ERROR;
    while ((t = nextToken(str, &item, &n)) != END) {
        f = 1;
        switch (t) {
        case PLUS:
            op = PLUS;
            break;
        case MINUS:
            op = MINUS;
            break;
        case NICHT:
            op = NICHT;
            break;
        default:
            //printf(" Exp=%d\n",term1);
            pushBack(str, n);
            return term1;
        }

        if (op == MINUS) {
            term2 = execITerm(str, bPtr, bitpos, pPtr, err) * -1;
        } else if (op == NICHT) {
            term2 = ~(execITerm(str, bPtr, bitpos, pPtr, err));
        } else if (op == PLUS) {
            term2 = execITerm(str, bPtr, bitpos, pPtr, err);
        } if (*err) {
            return 0;
        }
        //printf(" T2=%d\n",term2);
        term1 += term2;
    }

    //printf(" Exp=%d\n",term1);
    return term1;
}

int execITerm(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err)
{
    int factor1, factor2;
    int op;
    char *item;
    int n;

    //printf("execTerm: %s\n",*str);

    factor1 = execIFactor(str, bPtr, bitpos, pPtr, err);
    if (*err) {
        return 0;
    }

    while (1) {
        switch (nextToken(str, &item, &n)) {
        case MAL:
            op = MAL;
            break;
        case GETEILT:
            op = GETEILT;
            break;
        case MODULO:
            op = MODULO;
            break;
        case UND:
            op = UND;
            break;
        case ODER:
            op = ODER;
            break;
        case XOR:
            op = XOR;
            break;
        case SHL:
            op = SHL;
            break;
        case SHR:
            op = SHR;
            break;
        default:
            pushBack(str, n);
            //printf("  ret(%d)\n",factor1);
            return factor1;
        }

        factor2 = execIFactor(str, bPtr, bitpos, pPtr, err);

        if (*err) {
            return 0;
        }

        if (op == MAL) {
            factor1 *= factor2;
        } else if (op == GETEILT) {
            factor1 /= factor2;
        } else if (op == MODULO) {
            factor1 %= factor2;
        } else if (op == UND) {
            factor1 &= factor2;
        } else if (op == ODER) {
            factor1 |= factor2;
        } else if (op == XOR) {
            factor1 ^= factor2;
        } else if (op == SHL) {
            factor1 <<= factor2;
        } else if (op == SHR) {
            factor1 >>= factor2;
        } else {
            sprintf(err, "Error exec ITerm: Unknown token %d", op);
            return 0;
        }
    }
}

int execIFactor(char **str, unsigned char *bPtr, char bitpos, char *pPtr, char *err)
{
    char nstring[100];
    int expression;
    int factor;
    char *nPtr;
    char *item;
    char token;
    int n;

    //printf("execFactor: %s\n",*str);

    switch (nextToken(str, &item, &n)) {
    case BYTE0:
        return ((int)bPtr[0]) & 0xff;
    case BYTE1:
        return ((int)bPtr[1]) & 0xff;
    case BYTE2:
        return ((int)bPtr[2]) & 0xff;
    case BYTE3:
        return ((int)bPtr[3]) & 0xff;
    case BYTE4:
        return ((int)bPtr[4]) & 0xff;
    case BYTE5:
        return ((int)bPtr[5]) & 0xff;
    case BYTE6:
        return ((int)bPtr[6]) & 0xff;
    case BYTE7:
        return ((int)bPtr[7]) & 0xff;
    case BYTE8:
        return ((int)bPtr[8]) & 0xff;
    case BYTE9:
        return ((int)bPtr[9]) & 0xff;
    case BYTEA:
        return ((int)bPtr[10]) & 0xff;
    case BYTEB:
        return ((int)bPtr[11]) & 0xff;
    case BYTEC:
        return ((int)bPtr[12]) & 0xff;
    case BYTED:
        return ((int)bPtr[13]) & 0xff;
    case BYTEE:
        return ((int)bPtr[14]) & 0xff;
    case BYTEF:
        return ((int)bPtr[15]) & 0xff;
    case BITPOS:
        return ((int)bitpos) & 0xff;
    case PBYTE0:
        return ((int)pPtr[0]) & 0xff;
    case PBYTE1:
        return ((int)pPtr[1]) & 0xff;
    case PBYTE2:
        return ((int)pPtr[2]) & 0xff;
    case PBYTE3:
        return ((int)pPtr[3]) & 0xff;
    case PBYTE4:
        return ((int)pPtr[4]) & 0xff;
    case PBYTE5:
        return ((int)pPtr[5]) & 0xff;
    case PBYTE6:
        return ((int)pPtr[6]) & 0xff;
    case PBYTE7:
        return ((int)pPtr[7]) & 0xff;
    case PBYTE8:
        return ((int)pPtr[8]) & 0xff;
    case PBYTE9:
        return ((int)pPtr[9]) & 0xff;
    case PBYTEA:
        return ((int)pPtr[10]) & 0xff;
    case PBYTEB:
        return ((int)pPtr[11]) & 0xff;
    case PBYTEC:
        return ((int)pPtr[12]) & 0xff;
    case PBYTED:
        return ((int)pPtr[13]) & 0xff;
    case PBYTEE:
        return ((int)pPtr[14]) & 0xff;
    case PBYTEF:
        return ((int)pPtr[15]) & 0xff;
    case HEX:
        nPtr = nstring;
        memset(nstring, 0, sizeof(nstring));
        strcpy(nstring, "0x");
        nPtr += 2;
        token = nextToken(str, &item, &n);
        while ((token == DIGIT) || (token == HEXDIGIT)) {
            *nPtr++ = *item;
            token = nextToken(str, &item, &n);
        }
        pushBack(str, n);
        sscanf(nstring, "%i", &factor);
        return factor;
    case DIGIT:
        nPtr = nstring;
        do {
            *nPtr++ = *item;
        } while ((token = nextToken(str, &item, &n)) == DIGIT);
        // If a . follows, we have a decimal number
        if (token == PUNKT) {
            do {
                *nPtr++ = *item;
            } while ((token = nextToken(str, &item, &n)) == DIGIT);
        }
        pushBack(str, n);
        *nPtr = '\0';
        factor = atof(nstring);
        return factor;
    case KAUF:
        expression = execIExpression(str, bPtr, bitpos, pPtr, err);
        if (*err) {
            return 0;
        }
        if (nextToken(str, &item, &n) != KZU) {
            sprintf(err, "expected factor:) [%c]\n", *item);
            return 0;
        }
        return expression;
    case NICHT:
        return ~execIFactor(str, bPtr, bitpos, pPtr, err);
    default:
        sprintf(err, "expected factor: B0..BF P0..PF BP number ( ) [%c]\n", *item);
        return 0;
    }
}

int nextToken(char **str, char **c, int *count)
{
    char item;

    //printf("\tInput String:%s\n",*str);

    item = **str;
    while (isblank(item)) {
        item = *(++*str);
    }

    *c = *str;
    (*str)++;
    //printf("\t  Token: %c   [ %s ] \n",**c,*str);
    *count = 1;

    switch (**c) {
    case '+':
        return PLUS;
    case '-':
        return MINUS;
    case '*':
        return MAL;
    case '/':
        return GETEILT;
    case '%':
        return MODULO;
    case '(':
        return KAUF;
    case ')':
        return KZU;
    case 'V':
        return VALUE;
    case '^':
        return XOR;
    case '&':
        return UND;
    case '|':
        return ODER;
    case '~':
        return NICHT;
    case '0':
        if (*(*str) == 'x') {
            (*str)++;
            *count = 2;
            return HEX;
        }
        return DIGIT;
    case '<':
        *count = 2;
        switch (*(*str)++) {
        case '<' :
            return SHL;
        }
    case '>':
        *count = 2;
        switch (*(*str)++) {
        case '>':
            return SHR;
        }
    case 'B':
        *count = 2;
        switch (*(*str)++) {
        case '0':
            return BYTE0;
        case '1':
            return BYTE1;
        case '2':
            return BYTE2;
        case '3':
            return BYTE3;
        case '4':
            return BYTE4;
        case '5':
            return BYTE5;
        case '6':
            return BYTE6;
        case '7':
            return BYTE7;
        case '8':
            return BYTE8;
        case '9':
            return BYTE9;
        case 'A':
            return BYTEA;
        case 'B':
            return BYTEB;
        case 'C':
            return BYTEC;
        case 'D':
            return BYTED;
        case 'E':
            return BYTEE;
        case 'F':
            return BYTEF;
        case 'P':
            return BITPOS;
        }
    case 'P':
        *count = 2;
        switch (*(*str)++) {
        case '0':
            return PBYTE0;
        case '1':
            return PBYTE1;
        case '2':
            return PBYTE2;
        case '3':
            return PBYTE3;
        case '4':
            return PBYTE4;
        case '5':
            return PBYTE5;
        case '6':
            return PBYTE6;
        case '7':
            return PBYTE7;
        case '8':
            return PBYTE8;
        case '9':
            return PBYTE9;
        case 'A':
            return PBYTEA;
        case 'B':
            return PBYTEB;
        case 'C':
            return PBYTEC;
        case 'D':
            return PBYTED;
        case 'E':
            return PBYTEE;
        case 'F':
            return PBYTEF;
        }
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        return DIGIT;
    case 'a':
    case 'b':
    case 'c':
    case 'd':
    case 'e' :
    case 'f':
        return HEXDIGIT;
    case '.':
        return PUNKT;
    case '\0':
        return END;
    default:
        return ERROR;
    }
}

void  pushBack(char **str, int count)
{
    (*str) -= count;
    //printf("\t<<::%s\n",*str);
}
