/***************************************************************************
    copyright            : (C) 2005 by Inge Wallin
    email                : inge@lysator.liu.se
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "parser.h"

#include <ctype.h>

#include <QDebug>


Parser::Parser()
{
    start(QString());
}


Parser::Parser(const QString& _str)
{
    start(_str);
}


Parser::~Parser()
{
}


void Parser::start(const QString& _str)
{
    m_str = _str;

    if (_str.isNull()) {
        m_index     = -1;
        m_nextChar  = -1;
        m_nextToken = -1;
    } else {
        m_index     = 0;
        m_nextChar  = m_str.at(0).toLatin1();
        getNextToken();
    }
}


// ----------------------------------------------------------------


// Skip whitespace, and try to parse the following characters as an int.
//
// Return true if successful.  

bool Parser::parseInt(int *_result)
{
    int sign = 1;

    skipWhitespace();

    if (m_nextChar == '-') {
        sign = -1;
        getNextChar();
    }

    if (!isdigit(m_nextChar)) {
        return false;
    }

    int  result = 0;
    while (isdigit(m_nextChar)) {
        result = result * 10 + (m_nextChar - '0');
        getNextChar();
    }

    *_result = sign * result;
    return true;
}


// Skip whitespace, and try to parse the following characters as a
// simple float of the type -?[0-9]+'.'?[0-9]*
//
// Return true if successful.  

bool Parser::parseSimpleFloat(double *_result)
{
    double  sign = 1.0;

    skipWhitespace();
    if (m_nextChar == '-') {
        sign = -1.0;
        getNextChar();
    }

    if (!isdigit(m_nextChar)) {
        return false;
    }

    double result = 0.0;

    // The integer.
    while (isdigit(m_nextChar)) {
        result = result * 10.0 + (double)(m_nextChar - '0');
        getNextChar();
    }
    *_result = result;

    if (m_nextChar != '.' || !isdigit(getNextChar())) {
        *_result = sign * result;
        return true;
    }

    double  decimal = 0.1;
    while (isdigit(m_nextChar)) {
        result += decimal * (double)(m_nextChar - '0');
        decimal /= 10.0;
        getNextChar();
    }

    *_result = sign * result;
    return true;
}

// ----------------------------------------------------------------
//                           protected methods

int Parser::getNextChar()
{
//    qDebug() << "Parser::getNextChar(): char = " << m_nextChar;
//    qDebug() << "m_str.size() " << m_str.size()  << " with m_str: " << m_str  << " and m_index: " << m_index;

    ++m_index;

    if (m_index == -1) {
        return -1;
    }

    // If end of string, then reset the parser.
    if (m_index == m_str.size()) {
        m_index    = -1;
        m_nextChar = -1;
    } else {
        m_nextChar = m_str.at(m_index).toLatin1();
    }

    // Take care of null-terminated strings.
    if (m_nextChar == 0) {
        m_index    = -1;
        m_nextChar = -1;
    }

    return m_nextChar;
}


int Parser::skipWhitespace()
{
    while (QChar(m_nextChar).isSpace()) {
        getNextChar();
    }

    return m_nextChar;
}


// Get the next token.  This corresponds to the lexical analyzer of a
// standard parser, e.g as generated by lex.
//
// This basic parser supports integers and simple
// floats.  Reimplement this method to extend it.

int Parser::getNextToken()
{
    int saveIndex = m_index;

    skipWhitespace();
    if (isdigit(nextChar())) {
        // At this point we know that there is a valid number in the
        // string.  The only question now, is whether it is an int or a
        // float.

        parseInt(&m_intVal);

        skipWhitespace();
        if (nextChar() == '.') {
            m_index = saveIndex;

            // No need to check since we already know it is correct.
            (void) parseSimpleFloat(&m_floatVal);
            m_nextToken = FLOAT_TOKEN;
        } else {
            m_nextToken = INT_TOKEN;
        }
    } else if (nextChar() != -1) {
        // Any character.
        m_nextToken = nextChar();
        getNextChar();
    } else {
        // End of string.
        m_nextToken = -1;
    }

    return m_nextToken;
}
