#ifndef PARSERUTILS_H
#define PARSERUTILS_H

#include <cstring>
#include <cmath>
#include <limits>
#include <string>
#include <algorithm>

namespace lunasvg {

#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
#define IS_WS(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')

namespace Utils {

inline const char* rtrim(const char* start, const char* end)
{
    while(end > start && IS_WS(end[-1]))
        --end;

    return end;
}

inline const char* ltrim(const char* start, const char* end)
{
    while(start < end && IS_WS(*start))
        ++start;

    return start;
}

inline bool skipDesc(const char*& ptr, const char* end, const char ch)
{
    if(ptr >= end || *ptr != ch)
        return false;

    ++ptr;
    return true;
}

inline bool skipDesc(const char*& ptr, const char* end, const char* data)
{
    int read = 0;
    while(data[read])
    {
        if(ptr >= end || *ptr != data[read])
        {
            ptr -= read;
            return false;
        }

        ++read;
        ++ptr;
    }

    return true;
}

inline bool skipUntil(const char*& ptr, const char* end, const char ch)
{
    while(ptr < end && *ptr != ch)
        ++ptr;

    return ptr < end;
}

inline bool skipUntil(const char*& ptr, const char* end, const char* data)
{
    while(ptr < end)
    {
        auto start = ptr;
        if(skipDesc(start, end, data))
            break;
        ++ptr;
    }

    return ptr < end;
}

inline bool readUntil(const char*& ptr, const char* end, const char ch, std::string& value)
{
    auto start = ptr;
    if(!skipUntil(ptr, end, ch))
        return false;

    value.assign(start, ptr);
    return true;
}

inline bool readUntil(const char*& ptr, const char* end, const char* data, std::string& value)
{
    auto start = ptr;
    if(!skipUntil(ptr, end, data))
        return false;

    value.assign(start, ptr);
    return true;
}

inline bool skipWs(const char*& ptr, const char* end)
{
    while(ptr < end && IS_WS(*ptr))
       ++ptr;

    return ptr < end;
}

inline bool skipWsDelimiter(const char*& ptr, const char* end, const char delimiter)
{
    if(ptr < end && !IS_WS(*ptr) && *ptr != delimiter)
        return false;

    if(skipWs(ptr, end))
    {
        if(ptr < end && *ptr == delimiter)
        {
            ++ptr;
            skipWs(ptr, end);
        }
    }

    return ptr < end;
}

inline bool skipWsComma(const char*& ptr, const char* end)
{
    return skipWsDelimiter(ptr, end, ',');
}

inline bool isIntegralDigit(char ch, int base)
{
    if(IS_NUM(ch))
        return ch - '0' < base;

    if(IS_ALPHA(ch))
        return (ch >= 'a' && ch < 'a' + std::min(base, 36) - 10) || (ch >= 'A' && ch < 'A' + std::min(base, 36) - 10);

    return false;
}

template<typename T>
inline bool parseInteger(const char*& ptr, const char* end, T& integer, int base = 10)
{
    bool isNegative = 0;
    T value = 0;

    static const T intMax = std::numeric_limits<T>::max();
    static const bool isSigned = std::numeric_limits<T>::is_signed;
    using signed_t = typename std::make_signed<T>::type;
    const T maxMultiplier = intMax / static_cast<T>(base);

    if(ptr < end && *ptr == '+')
        ++ptr;
    else if(ptr < end && isSigned && *ptr == '-')
    {
        ++ptr;
        isNegative = true;
    }

    if(ptr >= end || !isIntegralDigit(*ptr, base))
        return false;

    do {
        const char ch = *ptr++;
        int digitValue;
        if(IS_NUM(ch))
            digitValue = ch - '0';
        else if(ch >= 'a')
            digitValue = ch - 'a' + 10;
        else
            digitValue = ch - 'A' + 10;

        if(value > maxMultiplier || (value == maxMultiplier && static_cast<T>(digitValue) > (intMax % static_cast<T>(base)) + isNegative))
            return false;
        value = static_cast<T>(base) * value + static_cast<T>(digitValue);
    } while(ptr < end && isIntegralDigit(*ptr, base));

    if(isNegative)
        integer = -static_cast<signed_t>(value);
    else
        integer = value;

    return true;
}

template<typename T>
inline bool parseNumber(const char*& ptr, const char* end, T& number)
{
    T integer, fraction;
    int sign, expsign, exponent;

    static const T numberMax = std::numeric_limits<T>::max();
    fraction = 0;
    integer = 0;
    exponent = 0;
    sign = 1;
    expsign = 1;

    if(ptr < end && *ptr == '+')
        ++ptr;
    else if(ptr < end && *ptr == '-')
    {
        ++ptr;
        sign = -1;
    }

    if(ptr >= end || !(IS_NUM(*ptr) || *ptr == '.'))
        return false;

    if(*ptr != '.')
    {
        do {
            integer = static_cast<T>(10) * integer + (*ptr - '0');
            ++ptr;
        } while(ptr < end && IS_NUM(*ptr));
    }

    if(ptr < end && *ptr == '.')
    {
        ++ptr;
        if(ptr >= end || !IS_NUM(*ptr))
            return false;

        T divisor = 1;
        do {
            fraction = static_cast<T>(10) * fraction + (*ptr - '0');
            divisor *= static_cast<T>(10);
            ++ptr;
        } while(ptr < end && IS_NUM(*ptr));
        fraction /= divisor;
    }

    if(ptr < end && (*ptr == 'e' || *ptr == 'E')
       && (ptr[1] != 'x' && ptr[1] != 'm'))
    {
        ++ptr;
        if(ptr < end && *ptr == '+')
            ++ptr;
        else if(ptr < end && *ptr == '-')
        {
            ++ptr;
            expsign = -1;
        }

        if(ptr >= end || !IS_NUM(*ptr))
            return false;

        do {
            exponent = 10 * exponent + (*ptr - '0');
            ++ptr;
        } while(ptr < end && IS_NUM(*ptr));
    }

    number = sign * (integer + fraction);
    if(exponent)
        number *= static_cast<T>(pow(10.0, expsign*exponent));

    return number >= -numberMax && number <= numberMax;
}

} // namespace Utils

} // namespace lunasvg

#endif // PARSERUTILS_H
