namespace WebCore {

const LChar kEndOfFileMarker = 0;

// CharacterProvider provides input characters to WebCore::HTMLTokenizer.
// It replaces WebCore::SegmentedString (which sits ontop of WTF::String).
class CharacterProvider {
public:
    CharacterProvider()
        : _totalBytes(0)
        , _remainingBytes(0)
        , _singleBytePtr(nullptr)
        , _doubleBytePtr(nullptr)
        , _littleEndian(false)
    {
    }

    void setContents(const LChar* str, size_t numberOfBytes)
    {
        _totalBytes = numberOfBytes;
        _remainingBytes = numberOfBytes;
        _singleBytePtr = str;
        _doubleBytePtr = nullptr;
        _littleEndian = false;
    }

    void setContents(const UChar* str, size_t numberOfBytes)
    {
        _totalBytes = numberOfBytes;
        _remainingBytes = numberOfBytes;
        _singleBytePtr = nullptr;
        _doubleBytePtr = str;
        _littleEndian = false;
    }

    void clear()
    {
        _totalBytes = 0;
        _remainingBytes = 0;
        _singleBytePtr = nullptr;
        _doubleBytePtr = nullptr;
        _littleEndian = false;
    }

    bool startsWith(const LChar* str,
                    size_t byteCount,
                    bool caseInsensitive = false) const
    {
        if (!str || byteCount > _remainingBytes)
            return false;

        for (size_t index = 0; index < byteCount; ++index) {
            UChar lhs = characterAtIndex(index);
            UChar rhs = str[index];

            if (caseInsensitive) {
                if (isASCIIUpper(lhs))
                    lhs = toLowerCase(lhs);

                if (isASCIIUpper(rhs))
                    rhs = toLowerCase(rhs);
            }

            if (lhs != rhs)
                return false;
        }

        return true;
    }

    inline UChar currentCharacter() const
    {
        return characterAtIndex(0);
    }

    inline UChar nextCharacter()
    {
        advanceBytePointer();
        return characterAtIndex(0);
    }

    inline void next()
    {
        advanceBytePointer();
    }

    inline bool isEmpty() const
    {
        return !_remainingBytes;
    }

    inline size_t remainingBytes() const
    {
        return _remainingBytes;
    }

    inline size_t bytesProvided() const
    {
        return _totalBytes - _remainingBytes;
    }

    inline void setLittleEndian()
    {
        _littleEndian = true;
    }

private:
    void advanceBytePointer()
    {
        --_remainingBytes;
        if (!_remainingBytes)
            return;

        if (_singleBytePtr)
            ++_singleBytePtr;
        else {
            DCHECK(_doubleBytePtr);
            ++_doubleBytePtr;
        }
    }

    UChar characterAtIndex(size_t index) const
    {
        if (!_remainingBytes) {
            // There is a quirk in the blink implementation wherein the empty state
            // is not set on the source until next() has been called when
            // _remainingBytes is zero. In this case, return kEndOfFileMarker.
            return kEndOfFileMarker;
        }

        ASSERT(_singleBytePtr || _doubleBytePtr);

        UChar character = kEndOfFileMarker;
        if (_singleBytePtr)
            character = _singleBytePtr[index];
        else
            character = _doubleBytePtr[index];

        if (_littleEndian)
            character = ByteSwap(character);

        return character;
    }

private:
    size_t _totalBytes;
    size_t _remainingBytes;
    const LChar* _singleBytePtr;
    const UChar* _doubleBytePtr;
    bool _littleEndian;

    DISALLOW_COPY_AND_ASSIGN(CharacterProvider);
};

}

#endif // IOS_THIRD_PARTY_BLINK_SRC_HTML_CHARACTER_PROVIDER_H_
