#ifndef DOC_VISITOR_H
#define DOC_VISITOR_H


#include <stack>
#include "tinyxml.h"
#include "DocStyle.h"
#include "DocTemplate.h"


class DocVisitor : public TiXmlVisitor
{
public:
	DocVisitor(DocOutputDevice* doc, DocStyle* style, DocTemplate* templ, int pageCount = -1);
	virtual ~DocVisitor();
	/// Visit a document.
	virtual bool VisitEnter( const TiXmlDocument& doc );
	/// Visit a document.
	virtual bool VisitExit( const TiXmlDocument& doc );
	/// Visit a declaration
	virtual bool Visit( const TiXmlDeclaration& declaration );
	/// Visit a stylesheet reference
	virtual bool Visit( const TiXmlStylesheetReference& stylesheet );
	/// Visit a comment node
	virtual bool Visit( const TiXmlComment& comment );
	/// Visit an unknow node
	virtual bool Visit( const TiXmlUnknown& unknown );
	
	/// Visit an element.
	virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
	/// Visit an element.
	virtual bool VisitExit( const TiXmlElement& element );
	/// Visit a text node
	virtual bool Visit( const TiXmlText& text );

  bool needsPageCount() const { return m_needPageCount; }
  int pageCount() const { return m_pageNum; }
private:
	DocOutputDevice* m_doc;
	DocOutputPage* m_page;

	void OutputText(const char* str);
	float OutputTextHelper(const char* str, bool doSplit = true);
	void OutputCurrentWord(const char* str);
	void CheckIfEndOfPage();
	void AddPage();
	void NewLine(float amount = 1.0);
  void DrawCodeBlockRect();
	void OutputTable();

  float WidestWordInText(std::string& str, bool& stretch);


  struct ListState
  {
    bool      ordered;
    int       index;
  };
  struct TextNode
  {
    int          listLevel;
    ListState    listState[16]; // up to 16 nested list levels 
    bool         codeFlag;
    bool         linkFlag;
    bool         blockQuoteFlag;
    bool         italicsFlag;
    bool         boldFlag;
    bool         preFlag;
    bool         tableFlag;
    FontType     style;
    std::string  words;
  };
  float bufferedTextWidth;
  std::vector<TextNode> bufferedText;
  void FlushBufferedText();

  bool redirect = false;
  struct RedirectedText
  {
    float        x;
    float        y;
    std::string  text;
  };
  std::vector<RedirectedText> redirectedText;
  void RedirectBounds(float& x1, float& y1, float& x2, float& y2);
  void DrawText(float x, float y, std::string str)
  {
    if (redirect)
      redirectedText.emplace_back(RedirectedText{x, y, str});
    else
      m_page->drawText(x, y, str.c_str());
  }
  void StartRedirect()
  {
    redirect = true;
  }
  void EndRedirect()
  {
    redirect = false;
    for (auto& t : redirectedText)
      DrawText(t.x, t.y, t.text);
    redirectedText.clear();
  }


	float x, y;
	DocStyle* m_style;
	DocTemplate* m_templ;
	std::stack<FontType> currentStyle;
	std::stack<int> currentHeadingLevel;
  std::string codeText;
	float indentLevels[32];
	int indentLevel;
	int insideCode;
	int insideLink;
  bool orderedList;
  int listItemIndex;
  bool startOfListItem;
  bool alignCenter;
	
  int insideBlockQuote;  // block quotes can be nested
  bool needBlockQuoteBar;
  int insidePreformatted;

  float codeStartX;
  float codeStartY;

  bool insideTable;
  enum TDAlign { TDA_Left, TDA_Center, TDA_Right };
  struct TD { TDAlign align; std::string str; };
  std::vector<std::vector<TD>> tableData;

  int m_columnNum;
  int m_columnCount;
  float m_columnSpacing;
  float m_columnWidth;
  float m_truePageLeftMargin;
  int m_pageNum;
  int m_pageCount;
  int m_needPageCount;
  float m_pageLeftMarginX;   // = 60.0f;
  float m_pageRightMarginX;  // = pageWidth - 60.0f;
  float m_pageTopMarginY;    // = 120.0f;
  float m_pageBottomMarginY; // = pageHeight - 60.0f;
};


#endif // DOC_VISITOR_H

