#ifndef __TINYHTMLPARSER_H__
#define __TINYHTMLPARSER_H__

#include <iostream>
#include <string>
#include <queue>
#include <stack>

namespace TinyHtmlParser
{

enum ElementType { ET_UNKNOWN = -1, ET_TAG = 0, ET_NODE, ET_ELEMENT };//0:just a tag, 1:no value, 2:have value

class CAttributeObject
{
public:
    CAttributeObject(const std::wstring& a, const std::wstring& v)
    : attr(a), value(v), next(NULL)
    {
    }
    virtual ~CAttributeObject() {}

    void Show(std::wostream& os) const;
public:
    std::wstring attr;
    std::wstring value;
    CAttributeObject* next;
};

class CElementObject
{
public:
    CElementObject();
    virtual ~CElementObject();

    virtual int Analyse();

    const CAttributeObject* FindAttribute(const std::wstring& attr) const;

    void Show(std::wostream& os) const;
protected:
    int AnalyseAttribute(const std::wstring& attr);
    int MakeAttribute(const std::wstring& attr);
    int MakeAttribute(const std::wstring& attr, const std::wstring& value);
    void FreeAnalyseAttribute();
    int AnalyseValue();
public:
    ElementType type; 
    size_t level;
    CElementObject* parent;
    CElementObject* child;
    CElementObject* sibling;

    CAttributeObject* attrib;
public:
    std::wstring tag;
    std::wstring value;
};

class CParserData
{
public:
    enum DataType { DT_UNKNOWN = -1, DT_TAG = 0, DT_VALUE, DT_END, DT_DONE, DT_TAG_VALUE };
public:
    CParserData()
        : type(DT_UNKNOWN)
        , start(0)
    {
    }
    virtual ~CParserData() {}

public:
    DataType type;
    size_t start;
    size_t end;
    size_t vstart;
    size_t vend;
};

class CDocumentObject
{
protected:
    static const wchar_t TAG_LT        =   L'<';
    static const wchar_t TAG_GT        =   L'>';
    static const wchar_t TAG_SLASH     =   L'/';
    static const wchar_t TAG_BSLASH    =   L'\\';
    static const wchar_t TAG_AND       =   L'&';

    typedef std::stack<CParserData> TDataStack;
    struct TNodeData
    {
        size_t level;
        CParserData tag;
        CParserData value;
//        CParserData end;
    };
    typedef std::deque<TNodeData> TNodeQueue;
public:
    typedef std::stack<const CElementObject*> TElementStack;
public:
    CDocumentObject();
    virtual ~CDocumentObject();

    int Load(const std::wstring& str);

    const CElementObject* Root() const;

    const CElementObject* FindFirstElement(const std::wstring& tag);
    const CElementObject* FindNextElement();

    const CElementObject* FindFirstElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);
    const CElementObject* FindNextElement(const CElementObject* element, const std::wstring& tag, TElementStack& tmpstack);

    const CAttributeObject* FindAttribute(const CElementObject* element, const std::wstring& attr);
    
    void Show(std::wostream& os) const;
protected:
    int PreProcess(const std::wstring& str, std::wstring& html);
    int PreParser(const std::wstring& html, TNodeQueue& que);
    int Parser(const std::wstring& html, TNodeQueue& que);
private:
    int PreParserLT(const std::wstring& html, std::wstring::size_type& pos, CParserData& data);
    int PushValueData(const CParserData& data, TDataStack& datastack) const;
    int PushTagData(const std::wstring& html, const CParserData& data, TDataStack& datatstack, TNodeQueue& nodeque) const;
    
    int CheckSpecialTag(const std::wstring& html, const CParserData& data) const;
    int CheckTag(const std::wstring& html, const CParserData& tag, const CParserData& end) const;
    CElementObject* MakeElement(const std::wstring& html, const TNodeData& node, CElementObject* parent, CElementObject* sibling) const;

    void CDocumentObject::ShowElement(std::wostream& os, const CElementObject* e) const;

    void FreeElement(CElementObject* root);

    const CElementObject* FindElement(const CElementObject* root, const CElementObject* pe, const std::wstring& tag, TElementStack& stack);
private:
    CElementObject* _root;
private:
    std::wstring _findtag;
    TElementStack _findstack;
};

}

#endif
