/**
   * Accepts a CSSPageNode and breaks down the node into different formatter nodes which should
   * represent it while rewriting the doc.<br>
   * The CSSPageNode will be broken down into several nodes of type FormatterCSSSelectorNode,
   * FormatterCSSBlockNode, and FormatterCSSDeclarationNode (if declarations are present).
   *
   * @param pageNode
   */
  private void pushFormatterPageNode(CSSPageNode pageNode) {
    // Push an At-Node to control the lines separators.
    FormatterCSSAtRuleNode atNode = new FormatterCSSAtRuleNode(document);
    // +5 for @page length
    int pageNodeStart = pageNode.getStartingOffset();
    atNode.setBegin(createTextNode(document, pageNodeStart, pageNodeStart + PAGE_AT_RULE_LENGTH));
    push(atNode);
    checkedPop(atNode, -1);

    CSSPageSelectorNode selector = pageNode.getSelector();
    CSSDeclarationNode[] declarations = pageNode.getDeclarations();
    int blockStartOffset = getBlockStartOffset(pageNodeStart + 1, document);

    if (selector != null) {
      blockStartOffset = getBlockStartOffset(selector.getEndingOffset() + 1, document);

      FormatterBlockWithBeginNode formatterSelectorNode =
          new FormatterCSSSelectorNode(document, true, false);
      formatterSelectorNode.setBegin(
          createTextNode(
              document,
              getBeginWithoutWhiteSpaces(pageNodeStart, document),
              getEndWithoutWhiteSpaces(pageNodeStart + PAGE_AT_RULE_LENGTH, document) + 1));
      push(formatterSelectorNode);
      checkedPop(formatterSelectorNode, -1);

      formatterSelectorNode = new FormatterCSSSelectorNode(document, false, false);
      // we do startingOffset - 1 to account for the ':'
      formatterSelectorNode.setBegin(
          createTextNode(
              document,
              getBeginWithoutWhiteSpaces(selector.getStartingOffset() - 1, document),
              getEndWithoutWhiteSpaces(selector.getEndingOffset() + 1, document) + 1));

      push(formatterSelectorNode);
      checkedPop(formatterSelectorNode, -1);
    }

    FormatterBlockWithBeginEndNode formatterBlockNode = new FormatterCSSBlockNode(document, false);
    formatterBlockNode.setBegin(createTextNode(document, blockStartOffset, blockStartOffset + 1));
    push(formatterBlockNode);

    // Don't create text nodes when there are no declarations, or only white space
    if (declarations != null
        && declarations.length != 0
        && getBeginWithoutWhiteSpaces(blockStartOffset + 1, document)
            < declarations[0].getStartingOffset()) {
      formatterBlockNode.addChild(
          createTextNode(document, blockStartOffset + 1, declarations[0].getStartingOffset()));
    }

    pushFormatterDeclarationNodes(pageNode.getEndingOffset(), declarations, formatterBlockNode);

    checkedPop(formatterBlockNode, -1);
    formatterBlockNode.setEnd(
        createTextNode(document, pageNode.getEndingOffset(), pageNode.getEndingOffset() + 1));
  }
  // This is a temporary fix for custom at-rules. When the parser adds support to return the ruleID,
  // this will need to
  // be changed
  private void pushAtRuleNode(CSSNode atRuleNode) {

    int length = document.getLength();
    int selectorStartingOffset = atRuleNode.getStartingOffset();
    int selectEndingOffset = atRuleNode.getEndingOffset();

    // Locate first white space after the @rule
    while (selectorStartingOffset < length) {
      if (Character.isWhitespace(document.charAt(selectorStartingOffset))) {
        break;
      }
      selectorStartingOffset++;
    }
    // Find the starting offset for the selector
    selectorStartingOffset = getBeginWithoutWhiteSpaces(selectorStartingOffset, document);

    // Find the end offset for the selector
    while (selectEndingOffset >= selectorStartingOffset) {
      if (!Character.isWhitespace(document.charAt(selectEndingOffset - 1))) {
        break;
      }
      selectEndingOffset--;
    }
    // Push an At-Node to control the lines separators.
    FormatterCSSAtRuleNode atNode = new FormatterCSSAtRuleNode(document);
    atNode.setBegin(
        createTextNode(document, atRuleNode.getStartingOffset(), selectorStartingOffset));
    push(atNode);
    checkedPop(atNode, -1);

    // We use a selector node for now, we may want to create a new formatter node type for rule id
    FormatterBlockWithBeginNode formatterSelectorNode =
        new FormatterCSSSelectorNode(document, false, false);
    formatterSelectorNode.setBegin(
        createTextNode(
            document,
            getBeginWithoutWhiteSpaces(selectorStartingOffset, document),
            getEndWithoutWhiteSpaces(selectEndingOffset, document) + 1));
    push(formatterSelectorNode);

    findAndPushPunctuationNode(TypePunctuation.SEMICOLON, selectEndingOffset, false);

    checkedPop(formatterSelectorNode, -1);
  }
  /**
   * Accepts a CSSMediaNode and breaks down the node into different formatter nodes which should
   * represent it while rewriting the doc.<br>
   * The statements of the media node will also be recursively added as formatter nodes.
   *
   * @param pageNode
   */
  private void pushFormatterMediaNode(CSSMediaNode mediaNode) {

    // Push an At-Node to control the lines separators.
    FormatterCSSAtRuleNode atNode = new FormatterCSSAtRuleNode(document);
    int mediaNodeStart = mediaNode.getStartingOffset();
    atNode.setBegin(
        createTextNode(document, mediaNodeStart, mediaNodeStart + MEDIA_AT_RULE_LENGTH));
    push(atNode);
    checkedPop(atNode, -1);

    CSSTextNode[] medias = mediaNode.getMedias();

    int blockStartOffset =
        getBlockStartOffset(medias[medias.length - 1].getEndingOffset() + 1, document);
    pushFormatterMediaSelectorNodes(medias, 0);

    FormatterBlockWithBeginEndNode formatterBlockNode = new FormatterCSSBlockNode(document, false);
    formatterBlockNode.setBegin(createTextNode(document, blockStartOffset, blockStartOffset + 1));

    push(formatterBlockNode);

    // Recursively add this node's children
    CSSNode[] statements = mediaNode.getStatements();
    addNodes(statements);

    if (statements.length > 0) {
      formatterBlockNode.addChild(
          createTextNode(
              document,
              statements[statements.length - 1].getEndingOffset() + 1,
              mediaNode.getEndingOffset()));
    }

    checkedPop(formatterBlockNode, -1);
    formatterBlockNode.setEnd(
        createTextNode(document, mediaNode.getEndingOffset(), mediaNode.getEndingOffset() + 1));
  }
  /**
   * Accepts a CSSFontFaceNode and breaks down the node into different formatter nodes which should
   * represent it while rewriting the doc.<br>
   * The CSSFontFaceNode will be broken down into several nodes of type FormatterCSSSelectorNode,
   * FormatterCSSBlockNode, and FormatterCSSDeclarationNode (if declarations are present).
   *
   * @param pageNode
   */
  private void pushFormatterFontFaceNode(CSSFontFaceNode faceFontNode) {

    CSSDeclarationNode[] declarations = faceFontNode.getDeclarations();
    int blockStartOffset = getBlockStartOffset(faceFontNode.getStartingOffset() + 9, document);

    // create a FormatterCSSSelectorNode for @font-face
    FormatterCSSAtRuleNode atFontFaceNode = new FormatterCSSAtRuleNode(document);
    atFontFaceNode.setBegin(
        createTextNode(
            document,
            getBeginWithoutWhiteSpaces(faceFontNode.getStartingOffset(), document),
            getEndWithoutWhiteSpaces(faceFontNode.getStartingOffset() + 9, document) + 1));
    push(atFontFaceNode);
    checkedPop(atFontFaceNode, -1);

    FormatterBlockWithBeginEndNode formatterBlockNode = new FormatterCSSBlockNode(document, false);
    formatterBlockNode.setBegin(createTextNode(document, blockStartOffset, blockStartOffset + 1));

    push(formatterBlockNode);

    // Don't create text nodes when there are no declarations, or only white space
    if (declarations != null
        && declarations.length != 0
        && getBeginWithoutWhiteSpaces(blockStartOffset + 1, document)
            < declarations[0].getStartingOffset()) {
      formatterBlockNode.addChild(
          createTextNode(document, blockStartOffset + 1, declarations[0].getStartingOffset()));
    }

    pushFormatterDeclarationNodes(faceFontNode.getEndingOffset(), declarations, formatterBlockNode);

    checkedPop(formatterBlockNode, -1);
    formatterBlockNode.setEnd(
        createTextNode(
            document, faceFontNode.getEndingOffset(), faceFontNode.getEndingOffset() + 1));
  }