require 'spec_helper'

module RubySpeech
  module GRXML
    describe Matcher do
      let(:grammar) { nil }

      subject { described_class.new grammar }

      describe "matching against an input string" do
        context "with a grammar that takes a single specific digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digit' do
              rule :id => 'digit' do
                '6'
              end
            end
          end

          it "should max-match '6'" do
            input = '6'
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '6',
                                              :interpretation => 'dtmf-6'
            expect(subject.match(input)).to eq(expected_match)
            expect(input).to eq('6')
          end

          it "should potentially match an empty buffer" do
            expect(subject.match('')).to eq(GRXML::PotentialMatch.new)
          end

          %w{1 2 3 4 5 7 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar with SISR tags" do
          let :grammar do
            RubySpeech::GRXML.draw mode: 'dtmf', root: 'options', tag_format: 'semantics/1.0-literals' do
              rule id: 'options', scope: 'public' do
                item do
                  one_of do
                    item do
                      tag { '1' }
                      '1'
                    end
                    item do
                      tag { 'bar' }
                      '2'
                    end
                    item do
                      tag { 'baz' }
                      '3'
                    end
                    item do
                      tag { 'lala' }
                      '4'
                    end
                  end
                end
              end
            end
          end

          it "should return the literal tag interpretation" do
            expected_match = GRXML::MaxMatch.new mode: :dtmf, confidence: 1,
              utterance: '2', interpretation: 'bar'
            expect(subject.match('2')).to eq(expected_match)
          end
        end

        context "with a grammar that takes two specific digits" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                '5 6'
              end
            end
          end

          it "should maximally match '56'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '56',
                                              :interpretation => 'dtmf-5 dtmf-6'
            expect(subject.match('56')).to eq(expected_match)
          end

          it "should potentially match '5'" do
            input = '5'
            expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            expect(input).to eq('5')
          end

          %w{* *7 #6 6* 1 2 3 4 6 7 8 9 10 65 57 46 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes star and a digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                '* 6'
              end
            end
          end

          it "should maximally match '*6'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '*6',
                                              :interpretation => 'dtmf-star dtmf-6'
            expect(subject.match('*6')).to eq(expected_match)
          end

          it "should potentially match '*'" do
            expect(subject.match('*')).to eq(GRXML::PotentialMatch.new)
          end

          %w{*7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes hash and a digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                '# 6'
              end
            end
          end

          it "should maximally match '#6'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '#6',
                                              :interpretation => 'dtmf-pound dtmf-6'
            expect(subject.match('#6')).to eq(expected_match)
          end

          it "should potentially match '#'" do
            expect(subject.match('#')).to eq(GRXML::PotentialMatch.new)
          end

          %w{* *6 #7 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes two specific digits, via a ruleref, and whitespace normalization" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                ruleref :uri => '#star'
                '" 6 "'
              end

              rule :id => 'star' do
                '" * "'
              end
            end
          end

          it "should maximally match '*6'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '*6',
                                              :interpretation => 'dtmf-star dtmf-6'
            expect(subject.match('*6')).to eq(expected_match)
          end

          it "should potentially match '*'" do
            expect(subject.match('*')).to eq(GRXML::PotentialMatch.new)
          end

          %w{*7 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a single digit alternative" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                one_of do
                  item { '6' }
                  item { '7' }
                end
              end
            end
          end

          it "should maximally match '6'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '6',
                                              :interpretation => 'dtmf-6'
            expect(subject.match('6')).to eq(expected_match)
          end

          it "should maximally match '7'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '7',
                                              :interpretation => 'dtmf-7'
            expect(subject.match('7')).to eq(expected_match)
          end

          %w{* # 1 2 3 4 5 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a double digit alternative" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                one_of do
                  item do
                    token { '6' }
                    token { '5' }
                  end
                  item do
                    token { '7' }
                    token { '2' }
                  end
                end
              end
            end
          end

          it "should maximally match '65'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '65',
                                              :interpretation => 'dtmf-6 dtmf-5'
            expect(subject.match('65')).to eq(expected_match)
          end

          it "should maximally match '72'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '72',
                                              :interpretation => 'dtmf-7 dtmf-2'
            expect(subject.match('72')).to eq(expected_match)
          end

          %w{6 7}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{* # 1 2 3 4 5 8 9 10 66 26 61 75}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a triple digit alternative" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                one_of do
                  item do
                    token { '6' }
                    token { '5' }
                    token { '2' }
                  end
                  item do
                    token { '7' }
                    token { '2' }
                    token { '8' }
                  end
                end
              end
            end
          end

          it "should maximally match '652'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '652',
                                              :interpretation => 'dtmf-6 dtmf-5 dtmf-2'
            expect(subject.match('652')).to eq(expected_match)
          end

          it "should maximally match '728'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '728',
                                              :interpretation => 'dtmf-7 dtmf-2 dtmf-8'
            expect(subject.match('728')).to eq(expected_match)
          end

          %w{6 65 7 72}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{* # 1 2 3 4 5 8 9 10 66 26 61 75 729 654}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes two specific digits with the second being an alternative" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                string '*'
                one_of do
                  item { '6' }
                  item { '7' }
                end
              end
            end
          end

          it "should maximally match '*6'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '*6',
                                              :interpretation => 'dtmf-star dtmf-6'
            expect(subject.match('*6')).to eq(expected_match)
          end

          it "should maximally match '*7'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '*7',
                                              :interpretation => 'dtmf-star dtmf-7'
            expect(subject.match('*7')).to eq(expected_match)
          end

          it "should potentially match '*'" do
            expect(subject.match('*')).to eq(GRXML::PotentialMatch.new)
          end

          %w{*8 #6 6* 1 2 3 4 5 6 7 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes two specific digits with the first being an alternative" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                one_of do
                  item { '6' }
                  item { '7' }
                end
                string '*'
              end
            end
          end

          it "should maximally match '6*'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '6*',
                                              :interpretation => 'dtmf-6 dtmf-star'
            expect(subject.match('6*')).to eq(expected_match)
          end

          it "should maximally match '7*'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '7*',
                                              :interpretation => 'dtmf-7 dtmf-star'
            expect(subject.match('7*')).to eq(expected_match)
          end

          %w{6 7}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          it "should potentially match '7'" do
            expect(subject.match('7')).to eq(GRXML::PotentialMatch.new)
          end

          %w{8* 6# *6 *7 1 2 3 4 5 8 9 10 66 26 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a specific digit, followed by a specific digit repeated an exact number of times" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                string '1'
                item :repeat => 2 do
                  '6'
                end
              end
            end
          end

          it "should maximally match '166'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '166',
                                              :interpretation => 'dtmf-1 dtmf-6 dtmf-6'
            expect(subject.match('166')).to eq(expected_match)
          end

          %w{1 16}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{1666 16666 17}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a specific digit repeated an exact number of times, followed by a specific digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                item :repeat => 2 do
                  '6'
                end
                string '1'
              end
            end
          end

          it "should maximally match '661'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '661',
                                              :interpretation => 'dtmf-6 dtmf-6 dtmf-1'
            expect(subject.match('661')).to eq(expected_match)
          end

          %w{6 66}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{61 6661 66661 71 771}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a specific digit, followed by a specific digit repeated within a range" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                string '1'
                item :repeat => 0..3 do
                  '6'
                end
              end
            end
          end

          it "should maximally match '1666'" do
            expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                              :confidence     => 1,
                                              :utterance      => '1666',
                                              :interpretation => 'dtmf-1 dtmf-6 dtmf-6 dtmf-6'
            expect(subject.match('1666')).to eq(expected_match)
          end

          {
            '1' => 'dtmf-1',
            '16' => 'dtmf-1 dtmf-6',
            '166' => 'dtmf-1 dtmf-6 dtmf-6',
          }.each_pair do |input, interpretation|
            it "should match '#{input}'" do
              expected_match = GRXML::Match.new :mode           => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{6 16666 17}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a a specific digit repeated within a range" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                item :repeat => 0..3 do
                  '6'
                end
              end
            end
          end

          {
            '' => '',
            '6' => 'dtmf-6',
            '66' => 'dtmf-6 dtmf-6',
          }.each_pair do |input, interpretation|
            it "should match '#{input}'" do
              expected_match = GRXML::Match.new :mode           => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          {
            '666' => 'dtmf-6 dtmf-6 dtmf-6',
          }.each_pair do |input, interpretation|
            it "should maximally match '#{input}'" do
              expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{6666 7}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a a specific digit repeated within a range, followed by specific digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                item :repeat => 0..3 do
                  '6'
                end
                string '1'
              end
            end
          end

          {
            '1' => 'dtmf-1',
            '61' => 'dtmf-6 dtmf-1',
            '661' => 'dtmf-6 dtmf-6 dtmf-1',
            '6661' => 'dtmf-6 dtmf-6 dtmf-6 dtmf-1'
          }.each_pair do |input, interpretation|
            it "should maximally match '#{input}'" do
              expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{6 66 666}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{66661 71}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a specific digit, followed by a specific digit repeated a minimum number of times" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                string '1'
                item :repeat => '2-' do
                  '6'
                end
              end
            end
          end

          {
            '166' => 'dtmf-1 dtmf-6 dtmf-6',
            '1666' => 'dtmf-1 dtmf-6 dtmf-6 dtmf-6',
            '16666' => 'dtmf-1 dtmf-6 dtmf-6 dtmf-6 dtmf-6'
          }.each_pair do |input, interpretation|
            it "should match '#{input}'" do
              expected_match = GRXML::Match.new :mode           => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{1 16}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{7 17}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a specific digit repeated a minimum number of times, followed by a specific digit" do
          let(:grammar) do
            GRXML.draw :mode => :dtmf, :root => 'digits' do
              rule :id => 'digits' do
                item :repeat => '2-' do
                  '6'
                end
                string '1'
              end
            end
          end

          {
            '661' => 'dtmf-6 dtmf-6 dtmf-1',
            '6661' => 'dtmf-6 dtmf-6 dtmf-6 dtmf-1',
            '66661' => 'dtmf-6 dtmf-6 dtmf-6 dtmf-6 dtmf-1'
          }.each_pair do |input, interpretation|
            it "should maximally match '#{input}'" do
              expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{6 66}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{7 71 61}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end

        context "with a grammar that takes a 4 digit pin terminated by hash, or the *9 escape sequence" do
          let(:grammar) do
            RubySpeech::GRXML.draw :mode => :dtmf, :root => 'pin' do
              rule :id => 'digit' do
                one_of do
                  ('0'..'9').map { |d| item { d } }
                end
              end

              rule :id => 'pin', :scope => 'public' do
                one_of do
                  item do
                    item :repeat => '4' do
                      ruleref :uri => '#digit'
                    end
                    "#"
                  end
                  item do
                    "\* 9"
                  end
                end
              end
            end
          end

          {
            '*9' => 'dtmf-star dtmf-9',
            '1234#' => 'dtmf-1 dtmf-2 dtmf-3 dtmf-4 dtmf-pound',
            '5678#' => 'dtmf-5 dtmf-6 dtmf-7 dtmf-8 dtmf-pound',
            '1111#' => 'dtmf-1 dtmf-1 dtmf-1 dtmf-1 dtmf-pound'
          }.each_pair do |input, interpretation|
            it "should maximally match '#{input}'" do
              expected_match = GRXML::MaxMatch.new :mode        => :dtmf,
                                                :confidence     => 1,
                                                :utterance      => input,
                                                :interpretation => interpretation
              expect(subject.match(input)).to eq(expected_match)
            end
          end

          %w{* 1 12 123 1234}.each do |input|
            it "should potentially match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::PotentialMatch.new)
            end
          end

          %w{11111 #1111 *7}.each do |input|
            it "should not match '#{input}'" do
              expect(subject.match(input)).to eq(GRXML::NoMatch.new)
            end
          end
        end
      end
    end # Grammar
  end # GRXML
end # RubySpeech
