/* Copyright 2014 the SumatraPDF project authors (see AUTHORS file).
   License: Simplified BSD (see COPYING.BSD) */

#ifndef HtmlPullParser_h
#define HtmlPullParser_h

#include "HtmlParserLookup.h"

struct AttrInfo {
    const char *      name;
    size_t            nameLen;
    const char *      val;
    size_t            valLen;

    bool NameIs(const char *s) const;
    bool NameIsNS(const char *s, const char *ns) const;
    bool ValIs(const char *s) const;
};

// TrivialHtmlParser needs to enumerate all attributes of an HtmlToken
class HtmlParser;

struct HtmlToken {
    friend HtmlParser;

    enum TokenType {
        StartTag,           // <foo>
        EndTag,             // </foo>
        EmptyElementTag,    // <foo/>
        Text,               // <foo>text</foo> => "text"
        Error
    };

    enum ParsingError {
        ExpectedElement,
        UnclosedTag,
        InvalidTag
    };

    bool IsStartTag() const { return type == StartTag; }
    bool IsEndTag() const { return type == EndTag; }
    bool IsEmptyElementEndTag() const { return type == EmptyElementTag; }
    bool IsTag() const { return IsStartTag() || IsEndTag() || IsEmptyElementEndTag(); }
    bool IsText() const { return type == Text; }
    bool IsError() const { return type == Error; }

    const char *GetReparsePoint() const;
    void SetTag(TokenType new_type, const char *new_s, const char *end);
    void SetError(ParsingError err, const char *errContext);
    void SetText(const char *new_s, const char *end);

    TokenType        type;
    ParsingError     error;
    const char *     s;
    size_t           sLen;

    // only for tags: type and name length
    HtmlTag          tag;
    size_t           nLen;

    bool             NameIs(const char *name) const;
    bool             NameIsNS(const char *name, const char *ns) const;
    AttrInfo *       GetAttrByName(const char *name);
    AttrInfo *       GetAttrByNameNS(const char *name, const char *attrNS);

protected:
    AttrInfo *       NextAttr();
    const char *     nextAttr;
    AttrInfo         attrInfo;
};

/* A very simple pull html parser. Call Next() to get the next HtmlToken,
which can be one one of 3 tag types or error. If a tag has attributes,
the caller has to parse them out (using HtmlToken::NextAttr()) */
class HtmlPullParser {
    const char *   currPos;
    const char *   end;

    const char *   start;
    size_t         len;

    HtmlToken      currToken;

public:
    HtmlPullParser(const char *s, size_t len) : currPos(s), end(s + len), start(s), len(len) { }
    HtmlPullParser(const char *s, const char *end) : currPos(s), end(end), start(s), len(end - s) { }

    void         SetCurrPosOff(ptrdiff_t off) { currPos = start + off; }
    size_t       Len()   const { return len;   }
    const char * Start() const { return start; }

    HtmlToken *  Next();
};

bool        SkipWs(const char*& s, const char *end);
bool        SkipNonWs(const char*& s, const char *end);
bool        SkipUntil(const char*& s, const char *end, char c);
bool        SkipUntil(const char*& s, const char *end, char *term);
bool        IsSpaceOnly(const char *s, const char *end);

int         HtmlEntityNameToRune(const char *name, size_t nameLen);
int         HtmlEntityNameToRune(const WCHAR *name, size_t nameLen);

const char *ResolveHtmlEntity(const char *s, size_t len, int& rune);
const char *ResolveHtmlEntities(const char *s, const char *end, Allocator *alloc);
char *      ResolveHtmlEntities(const char *s, size_t len);

#endif
