# encoding: UTF-8

# Copyright 2012 Twitter, Inc
# http://www.apache.org/licenses/LICENSE-2.0

require 'spec_helper'
require 'eprun'

describe TwitterCldr::Localized::LocalizedString do
  describe '#%' do
    context 'when argument is not a Hash' do
      it 'performs regular formatting of values' do
        expect('%d is an integer'.localize % 3.14).to eq('3 is an integer')
      end

      it 'performs regular formatting of arrays' do
        expect('"% 04d" is a %s'.localize % [12, 'number']).to eq('" 012" is a number')
      end

      it 'ignores pluralization placeholders' do
        expect('%s: %{horses_count:horses}'.localize % 'total').to eq('total: %{horses_count:horses}')
      end

      it 'raises ArgumentError when the string contains named placeholder' do
        expect { '%{msg}: %{horses_count:horses}'.localize % 'total' }.to raise_error(ArgumentError)
      end
    end

    context 'when argument is a Hash' do
      let(:horses) { { one: '1 horse', other: '%{horses_count} horses' } }

      before(:each) do
        allow(TwitterCldr::Formatters::Plurals::Rules).to receive(:rule_for) do |n, _|
          n == 1 ? :one : :other
        end
      end

      it 'interpolates named placeholders' do
        expect('%<num>.2f is a %{noun}'.localize % { num: 3.1415, noun: 'number' }).to eq('3.14 is a number')
      end

      it 'performs regular pluralization' do
        expect('%{horses_count:horses}'.localize % { horses_count: 2, horses: horses }).to eq('2 horses')
      end

      it 'performs inline pluralization' do
        string = '%<{ "horses_count": { "other": "%{horses_count} horses" } }>'.localize
        expect(string % { horses_count: 2 }).to eq('2 horses')
      end

      it 'performs both formatting and regular pluralization simultaneously' do
        string = '%{msg}: %{horses_count:horses}'.localize
        expect(string % { horses_count: 2, horses: horses, msg: 'result' }).to eq('result: 2 horses')
      end

      it 'performs both formatting and inline pluralization simultaneously' do
        string = '%{msg}: %<{"horses_count": {"other": "%{horses_count} horses"}}>'.localize
        expect(string % { horses_count: 2, msg: 'result' }).to eq('result: 2 horses')
      end

      it 'performs both formatted interpolation and inline pluralization simultaneously' do
        string = '%<number>d, %<{"horses_count": {"other": "%{horses_count} horses" }}>'.localize
        expect(string % { number: 3.14, horses_count: 2 }).to eq('3, 2 horses')
      end

      it 'leaves regular pluralization placeholders unchanged if not enough information given' do
        string = '%{msg}: %{horses_count:horses}'.localize
        expect(string % { msg: 'no pluralization' } ).to eq('no pluralization: %{horses_count:horses}')
      end

      it 'leaves inline pluralization placeholders unchanged if not enough information given' do
        string = '%<number>d, %<{"horses_count": {"one": "one horse"}}>'.localize
        expect(string % { number: 3.14, horses_count: 2 }).to eq('3, %<{"horses_count": {"one": "one horse"}}>')
      end

      it 'raises KeyError when value for a named placeholder is missing' do
        expect do
          '%{msg}: %{horses_count:horses}'.localize % { horses_count: 2, horses: horses }
        end.to raise_error(KeyError)
      end
    end
  end

  describe "#to_s" do
    it "should return a copy the base string" do
      string = "galoshes"
      result = string.localize.to_s

      expect(result).to eq(string)
      expect(result.equal?(string)).not_to eq(true)
    end
  end

  describe "#to_f" do
    it "should correctly parse a number with a thousands separator" do
      expect("1,300".localize.to_f).to eq(1300.0)
      expect("1.300".localize(:es).to_f).to eq(1300.0)
    end

    it "should correctly parse a number with a decimal separator" do
      expect("1.300".localize.to_f).to eq(1.3)
      expect("1,300".localize(:es).to_f).to eq(1.3)
    end

    it "should correctly parse a number with a thousands and a decimal separator" do
      expect("1,300.05".localize.to_f).to eq(1300.05)
      expect("1.300,05".localize(:es).to_f).to eq(1300.05)
    end

    it "should return zero if the string contains no numbers" do
      expect("abc".localize.to_f).to eq(0.0)
    end

    it "should return only the numbers at the beginning of the string if the string contains any non-numeric characters" do
      expect("1abc".localize.to_f).to eq(1.0)
      expect("a1bc".localize.to_f).to eq(0.0)
    end
  end

  describe "#to_i" do
    it "should chop off the decimal" do
      expect("1,300.05".localize.to_i).to eq(1300)
      expect("1.300,05".localize(:es).to_i).to eq(1300)
    end
  end

  describe "#normalize" do
    let(:string) { 'string' }
    let(:normalized_string) { 'normalized' }
    let(:localized_string) { string.localize }

    it 'returns a LocalizedString' do
      expect(localized_string.normalize).to be_an_instance_of(described_class)
    end

    it 'it uses NFD by default' do
      expect(Eprun).to receive(:normalize).with(string, :nfd).and_return(normalized_string)
      expect(localized_string.normalize.base_obj).to eq(normalized_string)
    end

    it "uses specified algorithm if there is any" do
      expect(Eprun).to receive(:normalize).with(string, :nfkd).and_return(normalized_string)
      expect(localized_string.normalize(using: :NFKD).base_obj).to eq(normalized_string)
    end

    it "raises an ArgumentError if passed an unsupported normalization form" do
      expect { localized_string.normalize(using: :blarg) }.to raise_error(ArgumentError)
    end
  end

  describe "#casefold" do
    it 'returns a LocalizedString' do
      str = "Weißrussland".localize.casefold
      expect(str).to be_an_instance_of(described_class)
      expect(str.to_s).to eq("weissrussland")
    end

    it 'does not pass the t option by default' do
      expect("Istanbul".localize.casefold.to_s).to eq("istanbul")
    end

    it 'uses the t option when explicitly given' do
      expect("Istanbul".localize.casefold(t: true).to_s).to eq("ıstanbul")
    end

    it 'uses t by default if the locale is tr (az not supported)' do
      expect("Istanbul".localize(:tr).casefold.to_s).to eq("ıstanbul")
    end
  end

  describe "#downcase" do
    it 'lowercases the string' do
      expect('FoObAr'.localize(:en).downcase.to_s).to eq('foobar')
    end
  end

  describe "#upcase" do
    it 'uppercases the string' do
      expect('foObAr'.localize(:en).upcase.to_s).to eq('FOOBAR')
    end
  end

  describe "#titlecase" do
    it 'titlecases the string' do
      expect('cats are cool'.localize(:en).titlecase.to_s).to(
        eq('Cats Are Cool')
      )
    end
  end

  describe "#each_sentence" do
    it "returns an enumerator if not passed a block" do
      expect("foo bar".localize.each_sentence).to be_a(Enumerator)
    end

    it "yields sentences as localized strings" do
      str = "Quick. Brown. Fox.".localize

      str.each_sentence do |sentence|
        expect(sentence).to be_a(described_class)
      end

      expect(str.each_sentence.to_a.map(&:to_s)).to eq([
        "Quick. ", "Brown. ", "Fox."
      ])
    end
  end

  describe "#code_points" do
    it "returns an array of Unicode code points for the string" do
      expect("español".localize.code_points).to eq([0x65, 0x73, 0x70, 0x61, 0xF1, 0x6F, 0x6C])
    end
  end

  describe "#each_char" do
    it "iterates over each character in the string if a block is given" do
      index = 0
      str = "twitter"

      str.localize.each_char do |char|
        expect(str.chars.to_a[index]).to eq(char)
        index += 1
      end
    end

    it "returns an enumerator if no block is given" do
      expect("twitter".localize.each_char).to be_a(Enumerator)
    end

    it "responds to a few other methods from Enumerable" do
      upcased = "twitter".localize.map { |letter| letter.upcase }
      expect(upcased.size).to eq(7)
      expect(upcased.join).to eq("TWITTER")

      expect("twitter".localize.inject("") { |str, letter| str = "#{letter}#{str}"; str }).to eq("rettiwt")
    end
  end

  describe "#to_yaml" do
    it "should be able to successfully roundtrip the string" do
      expect(YAML.load("coolio".localize.to_yaml)).to eq("coolio")
      expect(YAML.load("coolió".localize.to_yaml)).to eq("coolió")
    end
  end

  describe "#to_bidi" do
    it "should return an instance of TwitterCldr::Shared::Bidi" do
      expect("abc".localize.to_bidi).to be_a(TwitterCldr::Shared::Bidi)
    end
  end

  describe "#to_reordered_s" do
    it "should reverse the order a basic RTL string" do
      str = "{0} \331\210 {1}".gsub("{0}", "a").gsub("{1}", "b")
      chars = str.chars.to_a
      expect(chars.first).to eq("a")
      expect(chars.last).to eq("b")

      result = str.localize.to_reordered_s(direction: :RTL)
      result_chars = result.chars.to_a
      expect(result_chars.first).to eq("b")
      expect(result_chars.last).to eq("a")
    end
  end

  describe '#to_territory' do
    let(:code) { 'JP' }
    let(:territory) { code.localize.to_territory }

    it 'returns a territory' do
      expect(territory).to be_a(TwitterCldr::Shared::Territory)
      expect(territory.code).to eq(code)
    end
  end

  describe '#script' do
    it 'detects the script the majority of the string is written in' do
      expect('Кирилл'.localize.script).to eq('Cyrillic')
    end
  end

  describe '#scripts' do
    it 'detects all the scripts present in the string' do
      expect('くろねこさまボクシサン'.localize.scripts.sort).to(
        eq(%w(Hiragana Katakana))
      )
    end
  end
end
