require 'spec_helper'

module RubySpeech
  describe SSML do
    describe "#draw" do
      let(:doc) { Nokogiri::XML::Document.new }

      it "should create an SSML document" do
        expected_doc = SSML::Speak.new doc
        expect(SSML.draw).to eq(expected_doc)
        expect(SSML.draw.document.xpath('ns:speak', ns: 'http://www.w3.org/2001/10/synthesis').size).to eq(1)
      end

      it "can draw with a language" do
        expected_doc = SSML::Speak.new doc, language: 'pt-BR'
        expect(SSML.draw(language: 'pt-BR')).to eq(expected_doc)
      end

      describe "when the return value of the block is a string" do
        it "should be inserted into the document" do
          expected_doc = SSML::Speak.new(doc, :content => "Hi, I'm Fred")
          expect(SSML.draw { "Hi, I'm Fred" }).to eq(expected_doc)
        end
      end

      describe "when the return value of the block is not a string" do
        it "should not be inserted into the document" do
          expected_doc = SSML::Speak.new doc
          expect(SSML.draw { :foo }).to eq(expected_doc)
        end
      end

      describe "when inserting a string" do
        it "should work" do
          expected_doc = SSML::Speak.new(doc, :content => "Hi, I'm Fred")
          expect(SSML.draw { string "Hi, I'm Fred" }).to eq(expected_doc)
        end
      end

      it "should allow other SSML elements to be inserted in the document" do
        expected_doc = SSML::Speak.new doc
        expected_doc << SSML::Voice.new(doc, :gender => :male, :name => 'fred')
        expect(SSML.draw { voice :gender => :male, :name => 'fred' }).to eq(expected_doc)
      end

      it "should allow nested block return values" do
        expected_doc = SSML::Speak.new doc
        expected_doc << SSML::Voice.new(doc, :gender => :male, :name => 'fred', :content => "Hi, I'm Fred.")

        doc = RubySpeech::SSML.draw do
          voice :gender => :male, :name => 'fred' do
            "Hi, I'm Fred."
          end
        end
        expect(doc).to eq(expected_doc)
      end

      it "should allow nested SSML elements" do
        drawn_doc = RubySpeech::SSML.draw do
          voice :gender => :male, :name => 'fred' do
            string "Hi, I'm Fred. The time is currently "
            say_as :interpret_as => 'date', :format => 'dmy' do
              "01/02/1960"
            end
          end
        end

        voice = SSML::Voice.new(doc, :gender => :male, :name => 'fred', :content => "Hi, I'm Fred. The time is currently ")
        voice << SSML::SayAs.new(doc, :interpret_as => 'date', :format => 'dmy', :content => "01/02/1960")
        expected_doc = SSML::Speak.new doc
        expected_doc << voice
        expect(drawn_doc).to eq(expected_doc)
      end

      it "should allow accessing methods defined outside the block" do
        def foo
          'bar'
        end

        expected_doc = SSML::Speak.new doc, :content => foo
        expect(SSML.draw { string foo }).to eq(expected_doc)
      end

      describe 'cloning' do
        context 'SSML documents' do
          let :original do
            RubySpeech::SSML.draw do
              string "Hi, I'm Fred."
            end
          end

          subject { original.clone }

          it 'should match the contents of the original document' do
            expect(subject.to_s).to eq(original.to_s)
          end

          it 'should match the class of the original document' do
            expect(subject.class).to eq(original.class)
          end

          it 'should be equal to the original document' do
            expect(subject).to eq(original)
          end
        end
      end

      describe "embedding" do
        it "SSML documents" do
          doc1 = RubySpeech::SSML.draw do
            string "Hi, I'm Fred. The time is currently "
            say_as :interpret_as => 'date', :format => 'dmy' do
              "01/02/1960"
            end
          end

          doc2 = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              embed doc1
            end
          end

          expected_doc = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              string "Hi, I'm Fred. The time is currently "
              say_as :interpret_as => 'date', :format => 'dmy' do
                "01/02/1960"
              end
            end
          end

          expect(doc2).to eq(expected_doc)
        end

        it "SSML elements" do
          element = SSML::Emphasis.new(doc, :content => "HELLO?")

          doc = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              embed element
            end
          end

          expected_doc = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              emphasis do
                "HELLO?"
              end
            end
          end

          expect(doc).to eq(expected_doc)
        end

        it "strings" do
          string = "How now, brown cow?"

          doc = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              embed string
            end
          end

          expected_doc = RubySpeech::SSML.draw do
            voice :gender => :male, :name => 'fred' do
              string "How now, brown cow?"
            end
          end

          expect(doc).to eq(expected_doc)
        end
      end

      it "should properly escape string input" do
        drawn_doc = RubySpeech::SSML.draw do
          voice { string "I <3 nachos." }
          voice { "I <3 nachos." }
        end
        expected_doc = SSML::Speak.new doc
        2.times do
          expected_doc << SSML::Voice.new(doc, :native_content => "I <3 nachos.")
        end
        expect(drawn_doc).to eq(expected_doc)
      end

      it "should allow all permutations of possible nested SSML elements" do
        drawn_doc = RubySpeech::SSML.draw do
          string "Hello world."
          ssml_break
          audio :src => "hello" do
            string "HELLO?"
            ssml_break
            audio :src => "hello"
            emphasis
            prosody
            say_as :interpret_as => 'date'
            voice
          end
          emphasis do
            string "HELLO?"
            ssml_break
            audio :src => "hello"
            emphasis
            prosody
            say_as :interpret_as => 'date'
            voice
          end
          prosody :rate => :slow do
            string "H...E...L...L...O?"
            ssml_break
            audio :src => "hello"
            emphasis
            prosody
            say_as :interpret_as => 'date'
            voice
          end
          say_as :interpret_as => 'date', :format => 'dmy' do
            "01/02/1960"
          end
          voice :gender => :male, :name => 'fred' do
            string "Hi, I'm Fred. The time is currently "
            say_as :interpret_as => 'date', :format => 'dmy' do
              "01/02/1960"
            end
            ssml_break
            audio :src => "hello"
            emphasis do
              "I'm so old"
            end
            prosody :rate => :fast do
              "And yet so spritely!"
            end
            voice :age => 12 do
              "And I'm young Fred"
            end
          end
        end
        expected_doc = SSML::Speak.new(doc, :content => "Hello world.")
        expected_doc << SSML::Break.new(doc)
        audio = SSML::Audio.new(doc, :src => "hello", :content => "HELLO?")
        audio << SSML::Break.new(doc)
        audio << SSML::Audio.new(doc, :src => "hello")
        audio << SSML::Emphasis.new(doc)
        audio << SSML::Prosody.new(doc)
        audio << SSML::SayAs.new(doc, :interpret_as => 'date')
        audio << SSML::Voice.new(doc)
        expected_doc << audio
        emphasis = SSML::Emphasis.new(doc, :content => "HELLO?")
        emphasis << SSML::Break.new(doc)
        emphasis << SSML::Audio.new(doc, :src => "hello")
        emphasis << SSML::Emphasis.new(doc)
        emphasis << SSML::Prosody.new(doc)
        emphasis << SSML::SayAs.new(doc, :interpret_as => 'date')
        emphasis << SSML::Voice.new(doc)
        expected_doc << emphasis
        prosody = SSML::Prosody.new(doc, :rate => :slow, :content => "H...E...L...L...O?")
        prosody << SSML::Break.new(doc)
        prosody << SSML::Audio.new(doc, :src => "hello")
        prosody << SSML::Emphasis.new(doc)
        prosody << SSML::Prosody.new(doc)
        prosody << SSML::SayAs.new(doc, :interpret_as => 'date')
        prosody << SSML::Voice.new(doc)
        expected_doc << prosody
        expected_doc << SSML::SayAs.new(doc, :interpret_as => 'date', :format => 'dmy', :content => "01/02/1960")
        voice = SSML::Voice.new(doc, :gender => :male, :name => 'fred', :content => "Hi, I'm Fred. The time is currently ")
        voice << SSML::SayAs.new(doc, :interpret_as => 'date', :format => 'dmy', :content => "01/02/1960")
        voice << SSML::Break.new(doc)
        voice << SSML::Audio.new(doc, :src => "hello")
        voice << SSML::Emphasis.new(doc, :content => "I'm so old")
        voice << SSML::Prosody.new(doc, :rate => :fast, :content => "And yet so spritely!")
        voice << SSML::Voice.new(doc, :age => 12, :content => "And I'm young Fred")
        expected_doc << voice
        expect(drawn_doc).to eq(expected_doc)
      end
    end

    describe "importing nested tags" do
      let(:doc) { Nokogiri::XML::Document.new }
      let(:say_as) { SSML::SayAs.new doc, :interpret_as => 'date', :format => 'dmy', :content => "01/02/1960" }
      let(:string) { "Hi, I'm Fred. The time is currently " }
      let :voice do
        SSML::Voice.new(doc, :gender => :male, :name => 'fred', :content => string).tap do |voice|
          voice << say_as
        end
      end

      let :document do
        SSML::Speak.new(doc).tap { |doc| doc << voice }.to_s
      end

      let(:import) { SSML.import document }

      subject { import }

      it "should work" do
        expect { subject }.not_to raise_error
      end

      it { is_expected.to be_a SSML::Speak }

      its(:children) { should == [voice] }

      describe "voice" do
        subject { import.children.first }

        its(:children) { should == [string, say_as] }
      end
    end
  end
end
