// Copyright (c) 2017-2022 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/json/

#ifndef TAO_JSON_JAXN_INTERNAL_ERRORS_HPP
#define TAO_JSON_JAXN_INTERNAL_ERRORS_HPP

#include <string>

#include <tao/pegtl/normal.hpp>
#include <tao/pegtl/parse_error.hpp>
#include <tao/pegtl/rules.hpp>

#include "grammar.hpp"

namespace tao::json::jaxn::internal
{
   template< typename Rule >
   struct errors
      : pegtl::normal< Rule >
   {
      static const char* error_message;

      template< typename Input, typename... States >
      static void raise( const Input& in, States&&... /*unused*/ )
      {
         throw pegtl::parse_error( error_message, in );
      }

      template< template< typename... > class Action, typename Iterator, typename Input, typename... States >
      static auto apply( const Iterator& begin, const Input& in, States&&... st )
         -> decltype( Action< Rule >::apply( std::declval< const typename Input::action_t& >(), st... ) )
      {
         try {
            using action_t = typename Input::action_t;
            const action_t action_input( begin, in );
            return Action< Rule >::apply( action_input, st... );
         }
         catch( const pegtl::parse_error& ) {
            throw;
         }
         catch( const std::exception& e ) {
            throw pegtl::parse_error( e.what(), in );
         }
      }

      template< template< typename... > class Action, typename Input, typename... States >
      static auto apply0( const Input& in, States&&... st )
         -> decltype( Action< Rule >::apply0( st... ) )
      {
         try {
            return Action< Rule >::apply0( st... );
         }
         catch( const pegtl::parse_error& ) {
            throw;
         }
         catch( const std::exception& e ) {
            throw pegtl::parse_error( e.what(), in );
         }
      }
   };

   // clang-format off
   template<> inline const char* errors< rules::text >::error_message = "no valid JAXN";

   template<> inline const char* errors< rules::comment >::error_message = "expected '*' or '/'";
   template<> inline const char* errors< rules::line_comment_char >::error_message = "invalid character in line comment";
   template<> inline const char* errors< rules::block_comment_char >::error_message = "invalid character in block comment";
   template<> inline const char* errors< rules::end_block_comment >::error_message = "unterminated multi-line comment";

   template<> inline const char* errors< rules::end_array >::error_message = "incomplete array, expected value or ']'";
   template<> inline const char* errors< rules::end_object >::error_message = "incomplete object, expected key or '}'";
   template<> inline const char* errors< rules::name_separator >::error_message = "expected ':'";
   template<> inline const char* errors< rules::value >::error_message = "expected value";
   template<> inline const char* errors< rules::sor_value >::error_message = "expected value";
   template<> inline const char* errors< rules::sor_single_value >::error_message = "expected value";

   template<> inline const char* errors< rules::edigits >::error_message = "expected at least one exponent digit";
   template<> inline const char* errors< rules::fdigits >::error_message = "expected at least one fraction digit";
   template<> inline const char* errors< rules::kw_nan >::error_message = "invalid numeric literal";
   template<> inline const char* errors< rules::kw_infinity< false > >::error_message = "invalid numeric literal";
   template<> inline const char* errors< rules::kw_infinity< true > >::error_message = "invalid numeric literal";
   template<> inline const char* errors< rules::hexnum< false > >::error_message = "expected at least one hexadecimal digit";
   template<> inline const char* errors< rules::hexnum< true > >::error_message = "expected at least one hexadecimal digit";

   template<> inline const char* errors< rules::xdigit >::error_message = "incomplete universal character name";
   template<> inline const char* errors< rules::escaped >::error_message = "unknown escape sequence in string";
   template<> inline const char* errors< rules::chars< '"' > >::error_message = "invalid character in string";
   template<> inline const char* errors< rules::chars< '\'' > >::error_message = "invalid character in string";
   template<> inline const char* errors< rules::qstring_content< '"' > >::error_message = "unterminated string";
   template<> inline const char* errors< rules::qstring_content< '\'' > >::error_message = "unterminated string";
   template<> inline const char* errors< rules::mchars< '"' > >::error_message = "invalid character in multiline string";
   template<> inline const char* errors< rules::mchars< '\'' > >::error_message = "invalid character in multiline string";
   template<> inline const char* errors< rules::mqstring_content< '"' > >::error_message = "unterminated multiline string";
   template<> inline const char* errors< rules::mqstring_content< '\'' > >::error_message = "unterminated multiline string";
   template<> inline const char* errors< rules::string_fragment >::error_message = "expected string";

   template<> inline const char* errors< rules::bescaped >::error_message = "unknown escape sequence in binary string";
   template<> inline const char* errors< rules::bchars< '"' > >::error_message = "invalid character in binary string";
   template<> inline const char* errors< rules::bchars< '\'' > >::error_message = "invalid character in binary string";
   template<> inline const char* errors< rules::bqstring_content< '"' > >::error_message = "unterminated binary string";
   template<> inline const char* errors< rules::bqstring_content< '\'' > >::error_message = "unterminated binary string";
   template<> inline const char* errors< rules::binary_fragment >::error_message = "expected binary";
   template<> inline const char* errors< rules::bpart >::error_message = "expected a pair of hexadecimal digits";

   template<> inline const char* errors< pegtl::eof >::error_message = "unexpected character after JAXN value";
   // clang-format on

}  // namespace tao::json::jaxn::internal

#endif
