/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {HtmlAttrAst, HtmlElementAst, HtmlTextAst} from '@angular/compiler/src/html_ast';
import {HtmlParseTreeResult, HtmlParser} from '@angular/compiler/src/html_parser';
import {I18nHtmlParser} from '@angular/compiler/src/i18n/i18n_html_parser';
import {Message, id} from '@angular/compiler/src/i18n/message';
import {deserializeXmb} from '@angular/compiler/src/i18n/xmb_serializer';
import {InterpolationConfig} from '@angular/compiler/src/interpolation_config';
import {ParseError} from '@angular/compiler/src/parse_util';
import {humanizeDom} from '@angular/compiler/test/html_ast_spec_utils';
import {ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
import {StringMapWrapper} from '../../src/facade/collection';
export function main() {
  describe('I18nHtmlParser', () => {
    function parse(
        template: string, messages: {[key: string]: string}, implicitTags: string[] = [],
        implicitAttrs: {[k: string]: string[]} = {},
        interpolation?: InterpolationConfig): HtmlParseTreeResult {
      var parser = new Parser(new Lexer());
      let htmlParser = new HtmlParser();
      let msgs = '';
      StringMapWrapper.forEach(
          messages, (v: string, k: string) => msgs += `${v}`);
      let res = deserializeXmb(`${msgs}`, 'someUrl');
      return new I18nHtmlParser(
                 htmlParser, parser, res.content, res.messages, implicitTags, implicitAttrs)
          .parse(template, 'someurl', true, interpolation);
    }
    it('should delegate to the provided parser when no i18n', () => {
      expect(humanizeDom(parse('
a
', {}))).toEqual([
        [HtmlElementAst, 'div', 0], [HtmlTextAst, 'a', 1]
      ]);
    });
    it('should replace attributes', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('some message', 'meaning', null))] = 'another message';
      expect(
          humanizeDom(parse(
              '', translations)))
          .toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', 'another message']]);
    });
    it('should replace elements with the i18n attr', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('message', 'meaning', null))] = 'another message';
      expect(humanizeDom(parse('message
', translations))).toEqual([
        [HtmlElementAst, 'div', 0], [HtmlTextAst, 'another message', 1]
      ]);
    });
    it('should handle interpolation', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message(' and ', null, null))] =
          ' or ';
      expect(humanizeDom(parse('', translations)))
          .toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{{b}} or {{a}}']]);
    });
    it('should handle interpolation with config', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message(' and ', null, null))] =
          ' or ';
      expect(humanizeDom(parse(
                 '', translations, [], {},
                 {start: '{%', end: '%}'})))
          .toEqual([[HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', '{%b%} or {%a%}']]);
    });
    it('should handle interpolation with custom placeholder names', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message(' and ', null, null))] =
          ' or ';
      expect(
          humanizeDom(parse(
              ``,
              translations)))
          .toEqual([
            [HtmlElementAst, 'div', 0],
            [HtmlAttrAst, 'value', '{{b //i18n(ph="SECOND")}} or {{a //i18n(ph="FIRST")}}']
          ]);
    });
    it('should handle interpolation with duplicate placeholder names', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message(' and ', null, null))] =
          ' or ';
      expect(
          humanizeDom(parse(
              ``,
              translations)))
          .toEqual([
            [HtmlElementAst, 'div', 0],
            [HtmlAttrAst, 'value', '{{b //i18n(ph="FIRST")}} or {{a //i18n(ph="FIRST")}}']
          ]);
    });
    it('should handle nested html', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('ab', null, null))] =
          'BA';
      expect(humanizeDom(parse('', translations))).toEqual([
        [HtmlElementAst, 'div', 0],
        [HtmlElementAst, 'b', 1],
        [HtmlTextAst, 'B', 2],
        [HtmlElementAst, 'a', 1],
        [HtmlTextAst, 'A', 2],
      ]);
    });
    it('should support interpolation', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message(
          'ab', null,
          null))] = 'BA';
      expect(humanizeDom(parse('', translations))).toEqual([
        [HtmlElementAst, 'div', 0],
        [HtmlElementAst, 'b', 1],
        [HtmlTextAst, '{{i}}B', 2],
        [HtmlElementAst, 'a', 1],
        [HtmlTextAst, 'A', 2],
      ]);
    });
    it('should i18n attributes of placeholder elements', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('a', null, null))] = 'A';
      translations[id(new Message('b', null, null))] = 'B';
      expect(humanizeDom(parse('', translations)))
          .toEqual([
            [HtmlElementAst, 'div', 0],
            [HtmlElementAst, 'a', 1],
            [HtmlAttrAst, 'value', 'B'],
            [HtmlTextAst, 'A', 2],
          ]);
    });
    it('should preserve non-i18n attributes', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('message', null, null))] = 'another message';
      expect(humanizeDom(parse('message
', translations))).toEqual([
        [HtmlElementAst, 'div', 0], [HtmlAttrAst, 'value', 'b'],
        [HtmlTextAst, 'another message', 1]
      ]);
    });
    it('should extract from partitions', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('message1', 'meaning1', null))] = 'another message1';
      translations[id(new Message('message2', 'meaning2', null))] = 'another message2';
      let res = parse(
          `message1message2`,
          translations);
      expect(humanizeDom(res)).toEqual([
        [HtmlTextAst, 'another message1', 0],
        [HtmlTextAst, 'another message2', 0],
      ]);
    });
    it('should preserve original positions', () => {
      let translations: {[key: string]: string} = {};
      translations[id(new Message('ab', null, null))] =
          'BA';
      let res =
          (parse('', translations).rootNodes[0]).children;
      expect(res[0].sourceSpan.start.offset).toEqual(18);
      expect(res[1].sourceSpan.start.offset).toEqual(10);
    });
    describe('errors', () => {
      it('should error when giving an invalid template', () => {
        expect(humanizeErrors(parse('a', {}).errors)).toEqual([
          'Unexpected closing tag "b"'
        ]);
      });
      it('should error when no matching message (attr)', () => {
        let mid = id(new Message('some message', null, null));
        expect(humanizeErrors(parse('', {}).errors))
            .toEqual([`Cannot find message for id '${mid}', content 'some message'.`]);
      });
      it('should error when no matching message (text)', () => {
        let mid = id(new Message('some message', null, null));
        expect(humanizeErrors(parse('some message
', {}).errors)).toEqual([
          `Cannot find message for id '${mid}', content 'some message'.`
        ]);
      });
      it('should error when a non-placeholder element appears in translation', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('some message', null, null))] = 'a';
        expect(humanizeErrors(parse('some message
', translations).errors)).toEqual([
          `Unexpected tag "a". Only "ph" tags are allowed.`
        ]);
      });
      it('should error when a placeholder element does not have the name attribute', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('some message', null, null))] = 'a';
        expect(humanizeErrors(parse('some message
', translations).errors)).toEqual([
          `Missing "name" attribute.`
        ]);
      });
      it('should error when the translation refers to an invalid expression', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('hi ', null, null))] = 'hi ';
        expect(
            humanizeErrors(parse('', translations).errors))
            .toEqual(['Invalid interpolation name \'99\'']);
      });
    });
    describe('implicit translation', () => {
      it('should support attributes', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('some message', null, null))] = 'another message';
        expect(humanizeDom(parse('', translations, [], {
          'i18n-el': ['value']
        }))).toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
      });
      it('should support attributes with meaning and description', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('some message', 'meaning', 'description'))] = 'another message';
        expect(humanizeDom(parse(
                   '',
                   translations, [], {'i18n-el': ['value']})))
            .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
      });
      it('should support elements', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('message', null, null))] = 'another message';
        expect(humanizeDom(parse('message', translations, ['i18n-el'])))
            .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
      });
      it('should support elements with meaning and description', () => {
        let translations: {[key: string]: string} = {};
        translations[id(new Message('message', 'meaning', 'description'))] = 'another message';
        expect(humanizeDom(parse(
                   'message', translations,
                   ['i18n-el'])))
            .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
      });
    });
  });
}
function humanizeErrors(errors: ParseError[]): string[] {
  return errors.map(error => error.msg);
}