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

#ifndef TAOCPP_PEGTL_INCLUDE_CONTRIB_RAW_STRING_HPP
#define TAOCPP_PEGTL_INCLUDE_CONTRIB_RAW_STRING_HPP

#include "../apply_mode.hpp"
#include "../config.hpp"
#include "../nothing.hpp"
#include "../rewind_mode.hpp"

#include "../internal/iterator.hpp"
#include "../internal/must.hpp"
#include "../internal/skip_control.hpp"
#include "../internal/state.hpp"
#include "../internal/until.hpp"

#include "../analysis/generic.hpp"

namespace tao
{
   namespace TAOCPP_PEGTL_NAMESPACE
   {
      namespace internal
      {
         template< char Open, char Marker, char Close >
         struct raw_string_tag
         {
         };

         template< bool use_action, bool use_apply0, typename Tag >
         struct raw_string_state_apply;

         template< typename Tag >
         struct raw_string_state_apply< false, false, Tag >
         {
            template< template< typename... > class,
                      template< typename... > class,
                      typename State,
                      typename Input,
                      typename... States >
            static void success( const State&, const Input&, States&&... )
            {
            }
         };

         template< typename Tag >
         struct raw_string_state_apply< true, false, Tag >
         {
            template< template< typename... > class Action,
                      template< typename... > class Control,
                      typename State,
                      typename Input,
                      typename... States >
            static void success( const State& s, const Input& in, States&&... st )
            {
               Control< Tag >::template apply< Action >( s.iter, in, st... );
            }
         };

         template< typename Tag >
         struct raw_string_state_apply< true, true, Tag >
         {
            template< template< typename... > class Action,
                      template< typename... > class Control,
                      typename State,
                      typename Input,
                      typename... States >
            static void success( const State&, const Input& in, States&&... st )
            {
               Control< Tag >::template apply0< Action >( in, st... );
            }
         };

         template< typename Tag, typename Iterator >
         struct raw_string_state
         {
            template< typename Input, typename... States >
            raw_string_state( const Input&, States&&... )
            {
            }

            template< apply_mode A,
                      rewind_mode,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            void success( Input& in, States&&... st ) const
            {
               constexpr bool use_action = ( A == apply_mode::ACTION ) && ( !is_nothing< Action, Tag >::value );
               constexpr bool use_apply0 = use_action && has_apply0< Action< Tag >, type_list< States... > >::value;
               raw_string_state_apply< use_action, use_apply0, Tag >::template success< Action, Control >( *this, in, st... );
               in.bump_in_this_line( marker_size );
            }

            raw_string_state( const raw_string_state& ) = delete;
            void operator=( const raw_string_state& ) = delete;

            Iterator iter;
            std::size_t marker_size = 0;
         };

         template< char Open, char Marker >
         struct raw_string_open
         {
            using analyze_t = analysis::generic< analysis::rule_type::ANY >;

            template< apply_mode A,
                      rewind_mode,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename State >
            static bool match( Input& in, State& ls )
            {
               if( in.empty() || ( in.peek_char( 0 ) != Open ) ) {
                  return false;
               }
               for( std::size_t i = 1; i < in.size( i + 1 ); ++i ) {
                  switch( const auto c = in.peek_char( i ) ) {
                     case Open:
                        ls.marker_size = i + 1;
                        in.bump( ls.marker_size );
                        eol::match( in );
                        ls.iter = in.iterator();
                        return true;
                     case Marker:
                        break;
                     default:
                        return false;
                  }
               }
               return false;
            }
         };

         template< char Open, char Marker >
         struct skip_control< raw_string_open< Open, Marker > > : std::true_type
         {
         };

         template< char Marker, char Close >
         struct at_raw_string_close
         {
            using analyze_t = analysis::generic< analysis::rule_type::ANY >;

            template< apply_mode A,
                      rewind_mode,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename State >
            static bool match( Input& in, const State& ls )
            {
               if( in.size( ls.marker_size ) < ls.marker_size ) {
                  return false;
               }
               if( in.peek_char( 0 ) != Close ) {
                  return false;
               }
               if( in.peek_char( ls.marker_size - 1 ) != Close ) {
                  return false;
               }
               for( std::size_t i = 0; i < ls.marker_size - 2; ++i ) {
                  if( in.peek_char( i + 1 ) != Marker ) {
                     return false;
                  }
               }
               return true;
            }
         };

         template< char Marker, char Close >
         struct skip_control< at_raw_string_close< Marker, Close > > : std::true_type
         {
         };

         template< class Tag, typename... Rules >
         struct raw_string_switch_state
         {
            using analyze_t = analysis::generic< analysis::rule_type::SEQ, Rules... >;

            template< apply_mode A,
                      rewind_mode M,
                      template< typename... > class Action,
                      template< typename... > class Control,
                      typename Input,
                      typename... States >
            static bool match( Input& in, States&&... st )
            {
               using Iterator = typename Input::iterator_t;
               raw_string_state< Tag, Iterator > s( const_cast< const Input& >( in ), st... );

               if( duseltronik< seq< Rules... >, A, M, Action, Control >::match( in, s ) ) {
                  s.template success< A, M, Action, Control >( in, st... );
                  return true;
               }
               return false;
            }
         };

         template< typename Tag, typename... Rules >
         struct skip_control< raw_string_switch_state< Tag, Rules... > > : std::true_type
         {
         };

      }  // namespace internal

      // raw_string matches Lua-style long literals.
      //
      // The following description was taken from the Lua documentation
      // (see http://www.lua.org/docs.html):
      //
      // - An "opening long bracket of level n" is defined as an opening square
      //   bracket followed by n equal signs followed by another opening square
      //   bracket. So, an opening long bracket of level 0 is written as `[[`,
      //   an opening long bracket of level 1 is written as `[=[`, and so on.
      // - A "closing long bracket" is defined similarly; for instance, a closing
      //   long bracket of level 4 is written as `]====]`.
      // - A "long literal" starts with an opening long bracket of any level and
      //   ends at the first closing long bracket of the same level. It can
      //   contain any text except a closing bracket of the same level.
      // - Literals in this bracketed form can run for several lines, do not
      //   interpret any escape sequences, and ignore long brackets of any other
      //   level.
      // - For convenience, when the opening long bracket is immediately followed
      //   by a newline, the newline is not included in the string.
      //
      // Note that unlike Lua's long literal, a raw_string is customizable to use
      // other characters than `[`, `=` and `]` for matching. Also note that Lua
      // introduced newline-specific replacements in Lua 5.2, which we do not
      // support on the grammar level.

      template< char Open, char Marker, char Close >
      struct raw_string
         : internal::raw_string_switch_state< internal::raw_string_tag< Open, Marker, Close >,
                                              internal::raw_string_open< Open, Marker >,
                                              internal::must< internal::until< internal::at_raw_string_close< Marker, Close > > > >
      {
         // This is used to bind an action to the content.
         using content = internal::raw_string_tag< Open, Marker, Close >;

         // This is used for error-reporting when a raw string is not closed properly.
         using close = internal::until< internal::at_raw_string_close< Marker, Close > >;
      };

   }  // namespace TAOCPP_PEGTL_NAMESPACE

}  // namespace tao

#endif
