require 'gherkin/native'

module Gherkin
  module Lexer
    class <%= @i18n.underscored_iso_code.capitalize %> #:nodoc:
      native_impl('gherkin')

      %%{
        machine lexer;
        
        action begin_content {
          @content_start = p
          @current_line = @line_number
          @start_col = p - @last_newline - "#{@keyword}:".length
        }

        action start_docstring {
          @current_line = @line_number
          @start_col = p - @last_newline
        }
        
        action begin_docstring_content {
          @content_start = p
        }

        action start_docstring_content_type {
          @docstring_content_type_start = p
        }
        action end_docstring_content_type {
          @docstring_content_type_end = p
        }

        action store_docstring_content {
          con = unindent(@start_col, utf8_pack(data[@content_start...@next_keyword_start-1]).sub(/(\r?\n)?([\t ])*\Z/, '').gsub(/\\"\\"\\"/, '"""'))
          con_type = utf8_pack(data[@docstring_content_type_start...@docstring_content_type_end]).strip
          @listener.doc_string(con_type, con, @current_line) 
        }
        action store_feature_content {
          p = store_keyword_content(:feature, data, p, eof)
        }
 
        action store_background_content {
          p = store_keyword_content(:background, data, p, eof)
        }

        action store_scenario_content {
          p = store_keyword_content(:scenario, data, p, eof)
        }

        action store_scenario_outline_content {
          p = store_keyword_content(:scenario_outline, data, p, eof)
        }

        action store_examples_content {
          p = store_keyword_content(:examples, data, p, eof)
        }

        action store_step_content {
          con = utf8_pack(data[@content_start...p]).strip
          @listener.step(@keyword, con, @current_line)
        }
 
        action store_comment_content {
          con = utf8_pack(data[@content_start...p]).strip
          @listener.comment(con, @line_number)
          @keyword_start = nil
        }
 
        action store_tag_content {
          con = utf8_pack(data[@content_start...p]).strip
          @listener.tag(con, @current_line)
          @keyword_start = nil
        }
 
        action inc_line_number {
          @line_number += 1
        }
 
        action last_newline {
          @last_newline = p + 1
        }

        action start_keyword {
          @keyword_start ||= p
        }
 
        action end_keyword {
          @keyword = utf8_pack(data[@keyword_start...p]).sub(/:$/,'')
          @keyword_start = nil
        }
 
        action next_keyword_start {
          @next_keyword_start = p
        }

        action start_row {
          p = p - 1
          current_row = []
          @current_line = @line_number
        }

        action begin_cell_content {
          @content_start = p
        }

        action store_cell_content {
          con = utf8_pack(data[@content_start...p]).strip
          current_row << con.gsub(/\\\|/, "|").gsub(/\\n/, "\n").gsub(/\\\\/, "\\")
        }

        action store_row {
          @listener.row(current_row, @current_line)
        }

        action end_feature {
          if cs < lexer_first_final
            content = current_line_content(data, p)
            raise Gherkin::Lexer::LexingError.new("Lexing error on line %d: '%s'. See http://wiki.github.com/cucumber/gherkin/lexingerror for more information." % [@line_number, content])
          else
            @listener.eof
          end
        }

        include lexer_common "lexer_common.<%= @i18n.underscored_iso_code %>.rl"; 
      }%%
 
      def initialize(listener)
        @listener = listener

        # Initialize ivars to avoid warnings
        @keyword = nil
        %% write data;
      end
 
      def scan(data)
        data = (data + "\n%_FEATURE_END_%").unpack("c*") # Explicit EOF simplifies things considerably
        eof = pe = data.length
 
        @line_number = 1
        @last_newline = 0
 
        %% write init;
        %% write exec;
      end

      def unindent(startcol, text)
        text.gsub(/^[\t ]{0,#{startcol}}/, "")
      end

      def store_keyword_content(event, data, p, eof)
        end_point = (!@next_keyword_start or (p == eof)) ? p : @next_keyword_start
        content = unindent(@start_col + 2, utf8_pack(data[@content_start...end_point])).rstrip
        content_lines = content.split("\n")
        name = content_lines.shift || ""
        name.strip!
        description = content_lines.join("\n")
        @listener.__send__(event, @keyword, name, description, @current_line)
        @next_keyword_start ? @next_keyword_start - 1 : p
      ensure
        @next_keyword_start = nil
      end
      
      def current_line_content(data, p)
        rest = data[@last_newline..-1]
        utf8_pack(rest[0..rest.index(10)||-1]).strip # 10 is \n
      end

      if (RUBY_VERSION =~ /^1\.9|2\.0/)
        def utf8_pack(array)
          array.pack("c*").force_encoding("UTF-8")
        end
      else
        def utf8_pack(array)
          array.pack("c*")
        end
      end
    end
  end
end
