<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yiiunit\framework\i18n;

use yii\i18n\MessageFormatter;
use yiiunit\TestCase;

/**
 * @author Alexander Makarov <sam@rmcreative.ru>
 * @since 2.0
 * @group i18n
 */
class MessageFormatterTest extends TestCase
{
    const N = 'n';
    const N_VALUE = 42;
    const SUBJECT = 'сабж';
    const SUBJECT_VALUE = 'Answer to the Ultimate Question of Life, the Universe, and Everything';

    public function patterns()
    {
        return [
            [
                '{'.self::SUBJECT.'} is {'.self::N.', number}', // pattern
                self::SUBJECT_VALUE.' is '.self::N_VALUE, // expected
                [ // params
                    self::N => self::N_VALUE,
                    self::SUBJECT => self::SUBJECT_VALUE,
                ]
            ],

            [
                '{'.self::SUBJECT.'} is {'.self::N.', number, integer}', // pattern
                self::SUBJECT_VALUE.' is '.self::N_VALUE, // expected
                [ // params
                    self::N => self::N_VALUE,
                    self::SUBJECT => self::SUBJECT_VALUE,
                ]
            ],

            // This one was provided by Aura.Intl. Thanks!
            [<<<_MSG_
{gender_of_host, select,
  female {{num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to her party.}
      =2 {{host} invites {guest} and one other person to her party.}
     other {{host} invites {guest} and # other people to her party.}}}
  male {{num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to his party.}
      =2 {{host} invites {guest} and one other person to his party.}
     other {{host} invites {guest} and # other people to his party.}}}
  other {{num_guests, plural, offset:1
      =0 {{host} does not give a party.}
      =1 {{host} invites {guest} to their party.}
      =2 {{host} invites {guest} and one other person to their party.}
      other {{host} invites {guest} and # other people to their party.}}}}
_MSG_
                ,
                'ralph invites beep and 3 other people to his party.',
                [
                    'gender_of_host' => 'male',
                    'num_guests' => 4,
                    'host' => 'ralph',
                    'guest' => 'beep'
                ],
                defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '4.8', '<'),
                'select format is available in ICU > 4.4 and plural format with =X selector is avilable since 4.8'
            ],

            [
                '{name} is {gender} and {gender, select, female{she} male{he} other{it}} loves Yii!',
                'Alexander is male and he loves Yii!',
                [
                    'name' => 'Alexander',
                    'gender' => 'male',
                ],
                defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '4.4.2', '<'),
                'select format is available in ICU > 4.4'
            ],

            // verify pattern in select does not get replaced
            [
                '{name} is {gender} and {gender, select, female{she} male{he} other{it}} loves Yii!',
                'Alexander is male and he loves Yii!',
                [
                    'name' => 'Alexander',
                    'gender' => 'male',
                     // following should not be replaced
                    'he' => 'wtf',
                    'she' => 'wtf',
                    'it' => 'wtf',
                ],
                defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '4.4.2', '<'),
                'select format is available in ICU > 4.4'
            ],

            // verify pattern in select message gets replaced
            [
                '{name} is {gender} and {gender, select, female{she} male{{he}} other{it}} loves Yii!',
                'Alexander is male and wtf loves Yii!',
                [
                    'name' => 'Alexander',
                    'gender' => 'male',
                    'he' => 'wtf',
                    'she' => 'wtf',
                ],
                defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '4.8', '<'),
                'parameters in select format do not seem to work in ICU < 4.8'
            ],

            // some parser specific verifications
            [
                '{gender} and {gender, select, female{she} male{{he}} other{it}} loves {nr, number} is {gender}!',
                'male and wtf loves 42 is male!',
                [
                    'nr' => 42,
                    'gender' => 'male',
                    'he' => 'wtf',
                    'she' => 'wtf',
                ],
                defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '4.4.2', '<'),
                'select format is available in ICU > 4.4'
            ],

            // test ICU version compatibility
            [
                'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
                'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
                [],
            ],
            [
                'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
                'Showing <b>1-10</b> of <b>12</b> items.',
                [// A
                    'begin' => 1,
                    'end' => 10,
                    'count' => 10,
                    'totalCount' => 12,
                    'page' => 1,
                    'pageCount' => 2,
                ]
            ],
            [
                'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
                'Showing <b>1-1</b> of <b>1</b> item.',
                [// B
                    'begin' => 1,
                    'end' => 1,
                    'count' => 1,
                    'totalCount' => 1,
                    'page' => 1,
                    'pageCount' => 1,
                ]
            ],
            [
                'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.',
                'Showing <b>0-0</b> of <b>0</b> items.',
                [// C
                    'begin' => 0,
                    'end' => 0,
                    'count' => 0,
                    'totalCount' => 0,
                    'page' => 1,
                    'pageCount' => 1,
                ]
            ],
            [
                'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.',
                'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.',
                []
            ],
            [
                'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.',
                'Total <b>1</b> item.',
                [
                    'count' => 1,
                ]
            ],
            [
                'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.',
                'Total <b>1</b> item.',
                [
                    'begin' => 5,
                    'count' => 1,
                    'end' => 10,
                ]
            ],
            [
                '{0, plural, one {offer} other {offers}}',
                '{0, plural, one {offer} other {offers}}',
                [],
            ],
            [
                '{0, plural, one {offer} other {offers}}',
                'offers',
                [0],
            ],
            [
                '{0, plural, one {offer} other {offers}}',
                'offer',
                [1],
            ],
            [
                '{0, plural, one {offer} other {offers}}',
                'offers',
                [13],
            ],
        ];
    }

    public function parsePatterns()
    {
        return [
            [
                self::SUBJECT_VALUE.' is {0, number}', // pattern
                self::SUBJECT_VALUE.' is '.self::N_VALUE, // expected
                [ // params
                    0 => self::N_VALUE,
                ]
            ],

            [
                self::SUBJECT_VALUE.' is {'.self::N.', number}', // pattern
                self::SUBJECT_VALUE.' is '.self::N_VALUE, // expected
                [ // params
                    self::N => self::N_VALUE,
                ]
            ],

            [
                self::SUBJECT_VALUE.' is {'.self::N.', number, integer}', // pattern
                self::SUBJECT_VALUE.' is '.self::N_VALUE, // expected
                [ // params
                    self::N => self::N_VALUE,
                ]
            ],

            [
                "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
                "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
                [
                    0 => 4560,
                    1 => 123,
                    2 => 37.073
                ],
                'en-US'
            ],

            [
                "{0,number,integer} Affen auf {1,number,integer} Bäumen sind {2,number} Affen pro Baum",
                "4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum",
                [
                    0 => 4560,
                    1 => 123,
                    2 => 37.073
                ],
                'de',
            ],

            [
                "{monkeyCount,number,integer} monkeys on {trees,number,integer} trees make {monkeysPerTree,number} monkeys per tree",
                "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
                [
                    'monkeyCount' => 4560,
                    'trees' => 123,
                    'monkeysPerTree' => 37.073
                ],
                'en-US'
            ],

            [
                "{monkeyCount,number,integer} Affen auf {trees,number,integer} Bäumen sind {monkeysPerTree,number} Affen pro Baum",
                "4.560 Affen auf 123 Bäumen sind 37,073 Affen pro Baum",
                [
                    'monkeyCount' => 4560,
                    'trees' => 123,
                    'monkeysPerTree' => 37.073
                ],
                'de',
            ],
        ];
    }

    /**
     * @dataProvider patterns
     */
    public function testNamedArguments($pattern, $expected, $args, $skip = false, $skipMessage = '')
    {
        if ($skip) {
            $this->markTestSkipped($skipMessage);
        }
        $formatter = new MessageFormatter();
        $result = $formatter->format($pattern, $args, 'en-US');
        $this->assertEquals($expected, $result, $formatter->getErrorMessage());
    }

    /**
     * @dataProvider parsePatterns
     */
    public function testParseNamedArguments($pattern, $expected, $args, $locale = 'en-US')
    {
        if (!extension_loaded("intl")) {
            $this->markTestSkipped("intl not installed. Skipping.");
        }

        $formatter = new MessageFormatter();
        $result = $formatter->parse($pattern, $expected, $locale);
        $this->assertEquals($args, $result, $formatter->getErrorMessage() . ' Pattern: ' . $pattern);
    }

    public function testInsufficientArguments()
    {
        $expected = '{'.self::SUBJECT.'} is '.self::N_VALUE;

        $formatter = new MessageFormatter();
        $result = $formatter->format('{'.self::SUBJECT.'} is {'.self::N.', number}', [
            self::N => self::N_VALUE,
        ], 'en-US');

        $this->assertEquals($expected, $result, $formatter->getErrorMessage());
    }

    public function testNoParams()
    {
        $pattern = '{'.self::SUBJECT.'} is '.self::N;
        $formatter = new MessageFormatter();
        $result = $formatter->format($pattern, [], 'en-US');
        $this->assertEquals($pattern, $result, $formatter->getErrorMessage());
    }
}
