// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef MASTER_LEXER_STATE_H
#define MASTER_LEXER_STATE_H

#include <dns/master_lexer.h>

namespace isc {
namespace dns {

namespace master_lexer_internal {

/// \brief Tokenization state for \c MasterLexer.
///
/// This is a base class of classes that represent various states of a single
/// tokenization session of \c MasterLexer, i.e., the states used for a
/// single call to \c MasterLexer::getNextToken().
///
/// It follows the convention of the state design pattern: each derived class
/// corresponds to a specific state, and the state transition takes place
/// through the virtual method named \c handle().  The \c handle() method
/// takes the main \c MasterLexer object that holds all necessary internal
/// context, and updates it as necessary; each \c State derived class is
/// completely stateless.
///
/// The initial transition takes place in a static method of the base class,
/// \c start().  This is mainly for implementation convenience; we need to
/// pass options given to \c MasterLexer::getNextToken() for the initial
/// state, so it makes more sense to separate the interface for the transition
/// from the initial state.
///
/// If the whole lexer transition is completed within start(), it sets the
/// identified token and returns null; otherwise it returns a pointer to
/// an object of a specific state class that completes the session
/// on the call of handle().
///
/// As is usual in the state design pattern, the \c State class is made
/// a friend class of \c MasterLexer and can refer to its internal details.
/// This is intentional; essentially its a part of \c MasterLexer and
/// is defined as a separate class only for implementation clarity and better
/// testability.  It's defined in a publicly visible header, but that's only
/// for testing purposes.  No normal application or even no other classes of
/// this library are expected to use this class.
class State {
public:
    /// \brief Virtual destructor.
    ///
    /// In our usage this actually doesn't matter, but some compilers complain
    /// about it and we need to silence them.
    virtual ~State() {}

    /// \brief Begin state transitions to get the next token.
    ///
    /// This is the first method that \c MasterLexer needs to call for a
    /// tokenization session.  The lexer passes a reference to itself
    /// and options given in \c getNextToken().
    ///
    /// \throw MasterLexer::ReadError Unexpected I/O error
    /// \throw std::bad_alloc Internal resource allocation failure
    ///
    /// \param lexer The lexer object that holds the main context.
    /// \param options The options passed to getNextToken().
    /// \return A pointer to the next state object or null if the transition
    /// is completed.
    static const State* start(MasterLexer& lexer,
                              MasterLexer::Options options);

    /// \brief Handle the process of one specific state.
    ///
    /// This method is expected to be called on the object returned by
    /// start(). In the usual state transition design pattern, it would
    /// return the next state. But as we noticed, we never have another
    /// state, so we simplify it by not returning anything instead of
    /// returning null every time.
    ///
    /// \throw MasterLexer::ReadError Unexpected I/O error
    /// \throw std::bad_alloc Internal resource allocation failure
    ///
    /// \param lexer The lexer object that holds the main context.
    virtual void handle(MasterLexer& lexer) const = 0;

    /// \brief Types of states.
    ///
    /// Specific states are basically hidden within the implementation,
    /// but we'd like to allow tests to examine them, so we provide
    /// a way to get an instance of a specific state.
    enum ID {
        CRLF,                  ///< Just seen a carriage-return character
        String,                ///< Handling a string token
        QString,               ///< Handling a quoted string token
        Number                 ///< Handling a number
    };

    /// \brief Returns a \c State instance of the given state.
    ///
    /// This is provided only for testing purposes so tests can check
    /// the behavior of each state separately.  \c MasterLexer shouldn't
    /// need this method.
    static const State& getInstance(ID state_id);

    /// \name Read-only accessors for testing purposes.
    ///
    /// These allow tests to inspect some selected portion of the internal
    /// states of \c MasterLexer.  These shouldn't be used except for testing
    /// purposes.
    ///@{
    bool wasLastEOL(const MasterLexer& lexer) const;
    const MasterToken& getToken(const MasterLexer& lexer) const;
    size_t getParenCount(const MasterLexer& lexer) const;
    ///@}

protected:
    /// \brief An accessor to the internal implementation class of
    /// \c MasterLexer.
    ///
    /// This is provided for specific derived classes as they are not direct
    /// friends of \c MasterLexer.
    ///
    /// \param lexer The lexer object that holds the main context.
    /// \return A pointer to the implementation class object of the given
    /// lexer.  This is never null.
    MasterLexer::MasterLexerImpl* getLexerImpl(MasterLexer& lexer) const {
        return (lexer.impl_.get());
    }
};

} // namespace master_lexer_internal
} // namespace dns
} // namespace isc
#endif  // MASTER_LEXER_STATE_H
