/*
    SPDX-FileCopyrightText: 2009 Thomas McGuire <mcguire@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once

#include "headers_p.h"

#include <QList>

class QByteArray;

namespace KMime
{

namespace Headers
{
class Base;
}
namespace HeaderParsing
{

struct ParserState {
    const char *encodedWordRetryPoint = nullptr;
    bool brokenComment = false;
};

/**
  Parses the encoded word.

  @param scursor pointer to the first character beyond the initial '=' of
  the input string.
  @param send pointer to end of input buffer.
  @param result the decoded string the encoded work represented.
  @param usedCS    the used charset is returned here
  @param defaultCS the charset to use in case the detected
                   one isn't known to us.

  @return true if the input string was successfully decode; false otherwise.
*/
[[nodiscard]] bool parseEncodedWord(const char *&scursor, const char *const send, QString &result,
                 QByteArray &usedCS, const QByteArray &defaultCS, ParserState &state);

[[nodiscard]] QList<KMime::Headers::Base *> parseHeaders(const QByteArray &head);

enum ParseTokenFlag {
    ParseTokenNoFlag = 0,
    ParseTokenAllow8Bit = 1,
    ParseTokenRelaxedTText = 2
};
Q_DECLARE_FLAGS(ParseTokenFlags, ParseTokenFlag)

/** You may or may not have already started parsing into the
    token. This function will go on where you left off. */
[[nodiscard]] bool parseToken(const char *&scursor, const char *const send,
                              QByteArrayView &result, ParseTokenFlags flags = ParseTokenNoFlag);

/**
  Parses into the initial atom.
  You may or may not have already started parsing into the initial
  atom, but not up to it's end.

  @param scursor pointer to the first character beyond the initial '=' of
  the input string.
  @param send pointer to end of input buffer.
  @param result the parsed string.

  @return true if the input phrase was successfully parsed; false otherwise.
*/
[[nodiscard]] bool parseDotAtom(const char *&scursor, const char *const send,
                                QByteArrayView &result, NewlineType newline = NewlineType::LF);

/** @p scursor must be positioned after the opening openChar. */
[[nodiscard]] bool parseGenericQuotedString(const char *&scursor, const char *const send,
                         QString &result, NewlineType newline, ParserState &state,
                         const char openChar = '"', const char closeChar = '"', bool fillResult = true);

/** @p scursor must be positioned right after the opening '(' */
[[nodiscard]] bool parseComment(const char *&scursor, const char *const send, QString &result, ParserState &state,
             NewlineType newline = NewlineType::LF, bool reallySave = true);

/**
  Parses a phrase.

  You may or may not have already started parsing into the phrase, but
  only if it starts with atext. If you setup this function to parse a
  phrase starting with an encoded-word or quoted-string, @p scursor has
  to point to the char introducing the encoded-word or quoted-string, resp.

  @param scursor pointer to the first character beyond the initial '=' of
  the input string.
  @param send pointer to end of input buffer.
  @param result the parsed string.

  @param state Parser state between different calls on the same header.
  Use when possible to prevent quadratic behavior on invalid nested
  comments.

  @return true if the input phrase was successfully parsed; false otherwise.
*/
[[nodiscard]] bool parsePhrase(const char *&scursor, const char *const send, QString &result, NewlineType newline, ParserState &state);

/*!
  Eats comment-folding-white-space, skips whitespace, folding and comments
  (even nested ones) and stops at the next non-CFWS character.  After
  calling this function, you should check whether \a scursor == \a send
  (end of header reached).

  If a comment with unbalanced parentheses is encountered, \a scursor
  is being positioned on the opening '(' of the outmost comment.

  \a scursor pointer to the first character beyond the initial '=' of
  the input string.

  \a send pointer to end of input buffer.

  \a newline whether the input string is terminated with CRLF or LF.

  \a state Parser state between different calls on the same header.
  Use when possible to prevent quadratic behavior on invalid nested
  comments.
*/
void eatCFWS(const char *&scursor, const char *const send, NewlineType newline, ParserState &state);


/**
 * Extract the charset embedded in the parameter list if there is one.
 *
 * @since 4.5
 */
[[nodiscard]] bool parseParameterListWithCharset(const char *&scursor, const char *const send,
                              KMime::Headers::ParameterMap &result,
                              QByteArray &charset, ParserState &state, NewlineType newline = NewlineType::LF);

[[nodiscard]] bool parseMailbox(const char *&scursor, const char *const send, Types::Mailbox &result, NewlineType newline, ParserState &state);
[[nodiscard]] bool parseGroup(const char *&scursor, const char *const send, Types::Address &result, NewlineType newline, ParserState &state);
[[nodiscard]] bool parseAddress(const char *&scursor, const char *const send, Types::Address &result, NewlineType newline, ParserState &state);

[[nodiscard]] bool parseDomain(const char *&scursor, const char *const send,
                               QString &result, ParserState &state, NewlineType newline = NewlineType::LF);

[[nodiscard]] bool parseObsRoute(const char *&scursor, const char *const send, QStringList &result, ParserState &state,
                                 NewlineType newline = NewlineType::LF, bool save = false);

[[nodiscard]] bool parseAddrSpec(const char *&scursor, const char *const send, Types::AddrSpec &result, NewlineType newline, ParserState &state);

[[nodiscard]] bool parseAngleAddr(const char *&scursor, const char *const send, Types::AddrSpec &result, NewlineType newline, ParserState &state);

/**
  Parses an integer number.
  @param scursor pointer to the first character of the input string
  @param send pointer to end of input buffer
  @param result the parsing result
  @returns The number of parsed digits (don't confuse with @p result!)
*/
[[nodiscard]] int parseDigits(const char *&scursor, const char *const send, int &result);

[[nodiscard]] bool parseTime(const char *&scursor, const char *const send, int &hour, int &min,
          int &sec, long int &secsEastOfGMT, bool &timeZoneKnown,
          NewlineType newline = NewlineType::LF);

[[nodiscard]] bool parseDateTime(const char *&scursor, const char *const send,
                                QDateTime &result, NewlineType newline = NewlineType::LF);
[[nodiscard]] bool parseQDateTime(const char *&scursor, const char *const send,
                                QDateTime &result, NewlineType newline = NewlineType::LF);

}

}

Q_DECLARE_OPERATORS_FOR_FLAGS(KMime::HeaderParsing::ParseTokenFlags)
