/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2005 by Michiel van der Kolk 
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "databox.h"
#include "edittoken.h"
#include "editparser.h"

struct token *currenttoken,*lasttoken,*tokenstream;
int currentindex;
int lparencount,acceptedmask;
int tokensleft;
int invalid;
int invalid_mode;

void check_accepted(struct token *tstream, int count) {
    parse_stream(tstream,count+1,INVALID_EXPERT);
}

void parse_stream(struct token *tstream, int count, int inv_mode) {
    invalid_mode=inv_mode;
    acceptedmask=0;
    lparencount=0;
    tokensleft=count;
    currentindex=0;
    invalid=0;
    tokenstream=tstream;
    currenttoken=&tokenstream[currentindex];
    parseMExpr();
}

int check_tokenstream(struct token *tstream, int inv_mode) {
    int inval=0;
    int i;
    parse_stream(tstream,-1,inv_mode);
    inval=invalid;
    while( (inv_mode==INVALID_STRIP||inv_mode==INVALID_MARK) && invalid)
        parse_stream(tstream,-1,inv_mode);
    i=0;
    while(tstream[i].kind!=TOKEN_EOF)
        if(tstream[i++].kind==TOKEN_INVALID) {
          inval=1;
          break;
        }
    return inval==0;
}


void parse_accept_rparen(void) {
    if(!tokensleft) return;
    if(lparencount) {
        acceptedmask|=ACCEPT_RPAREN;
    }
}

void parse_accept(int bitmask) {
    if(!tokensleft) return;
    acceptedmask|=bitmask;
    if(lparencount) {
        acceptedmask&=~ACCEPT_EOF;
    }
}

void parse_checktoken() {
    int ok=0;
    if(!tokensleft) return;
    lasttoken=currenttoken;
    switch(lasttoken->kind) {
         case TOKEN_EOF:
              ok=acceptedmask&ACCEPT_EOF;
              break;
         case TOKEN_NOT:
              ok=acceptedmask&ACCEPT_NOT;
              break;
         case TOKEN_AND:
         case TOKEN_OR:
              ok=acceptedmask&ACCEPT_BOOLOP;
              break;
         case TOKEN_GT:
         case TOKEN_GTE:
         case TOKEN_LT:
         case TOKEN_LTE:
         case TOKEN_NE:
         case TOKEN_EQ:
              ok=acceptedmask&ACCEPT_NUMOP;
              break;
         case TOKEN_EQUALS:
         case TOKEN_CONTAINS:
         case TOKEN_STARTSWITH:
         case TOKEN_ENDSWITH:
              ok=acceptedmask&ACCEPT_STROP;
              break;
         case TOKEN_STRING:
         case TOKEN_STRINGIDENTIFIER:
              ok=acceptedmask&ACCEPT_STRARG;
              break;
         case TOKEN_NUM:
         case TOKEN_NUMIDENTIFIER:
              ok=acceptedmask&ACCEPT_NUMARG;
              break;
         case TOKEN_LPAREN:
              ok=acceptedmask&ACCEPT_LPAREN;
              if(ok) lparencount++;
              break;
         case TOKEN_RPAREN:
              ok=acceptedmask&ACCEPT_RPAREN;
              if(ok) lparencount--;
              break;
         case TOKEN_INVALID:
              if(invalid_mode!=INVALID_STRIP)
                  ok=1;
              break;
    }
    tokensleft--;
    if(lasttoken->kind==TOKEN_EOF)
            tokensleft=0;
    if(!ok&&tokensleft) { 
        // delete token
        int i=currentindex;
        //printf("Syntax error. accepted: 0x%x index:%d token: %d %s\n",acceptedmask,currentindex,currenttoken->kind,tokentostring(currenttoken));
        switch (invalid_mode) {
            case INVALID_STRIP:        
                do {
                   rb->memcpy(currenttoken,&tokenstream[++i],sizeof(struct token));;
                   currenttoken=&tokenstream[i];
                } while (currenttoken->kind!=TOKEN_EOF);
                currenttoken=&tokenstream[currentindex];
                break;
            case INVALID_MARK:
                currenttoken->kind=TOKEN_INVALID;
                break;
        }
        tokensleft=0;
        invalid=1;
    }
    if(tokensleft) {
        currenttoken=&tokenstream[++currentindex];
        acceptedmask=0;
    }
}

void parseCompareNum() {
    parse_accept(ACCEPT_NUMOP);
    parse_checktoken();
    parse_accept(ACCEPT_NUMARG);
    parse_checktoken();
}

void parseCompareString() {
    parse_accept(ACCEPT_STROP);
    parse_checktoken();
    parse_accept(ACCEPT_STRARG);
    parse_checktoken();
}

void parseExpr() {
    if(!tokensleft) return;
    parse_accept(ACCEPT_NOT|ACCEPT_LPAREN|ACCEPT_NUMARG|ACCEPT_STRARG);
    parse_checktoken();
    switch(lasttoken->kind) {
        case TOKEN_NOT:
            parseExpr();
            break;
        case TOKEN_LPAREN:
            parseMExpr();
            break;
        case TOKEN_NUM:
        case TOKEN_NUMIDENTIFIER:
            parseCompareNum();
            break;
        case TOKEN_STRING:
        case TOKEN_STRINGIDENTIFIER:
            parseCompareString();
            break;
    }
}

void parseMExpr() {
    parseExpr();
    parse_accept_rparen();
    parse_accept(ACCEPT_BOOLOP|ACCEPT_EOF);
    parse_checktoken();
    while(lasttoken->kind==TOKEN_OR || lasttoken->kind == TOKEN_AND) {
        parseExpr();
        parse_accept_rparen();
        parse_accept(ACCEPT_BOOLOP|ACCEPT_EOF);
        parse_checktoken();
        if(!tokensleft)
                return;
    }
}
