import javascript from '../../../src/js/parser/javascript/grammar.js';
import Node from '../../../src/js/parser/node.js';
import util from '../../../src/js/util.js';
import Snap from 'snapsvg-cjs';
import { testEach } from '../../helpers.js';

describe('parser/javascript/charset.js', function() {

  testEach(
    'Charset',
    {
      '[abc]': {
        label: 'One of:',
        elements: [
          jasmine.objectContaining({ type: 'literal', textValue: 'a' }),
          jasmine.objectContaining({ type: 'literal', textValue: 'b' }),
          jasmine.objectContaining({ type: 'literal', textValue: 'c' }),
        ],
      },
      '[^abc]': {
        label: 'None of:',
        elements: [
          jasmine.objectContaining({ type: 'literal', textValue: 'a' }),
          jasmine.objectContaining({ type: 'literal', textValue: 'b' }),
          jasmine.objectContaining({ type: 'literal', textValue: 'c' }),
        ],
      },
      '[aaa]': {
        label: 'One of:',
        elements: [
          jasmine.objectContaining({ type: 'literal', textValue: 'a' }),
        ],
      },
      '[a-z]': {
        label: 'One of:',
        elements: [
          jasmine.objectContaining({ type: 'charset-range', textValue: 'a-z' }),
        ],
      },
      '[\\b]': {
        label: 'One of:',
        elements: [
          jasmine.objectContaining({ type: 'charset-escape', textValue: '\\b' }),
        ],
      },

    },
    str => {
      const parser = new javascript.Parser(str);
      return parser.__consume__charset();
    },
  );

  it('adds a warning for character sets the contain non-standard escapes', function() {
    Node.state = { warnings: [] };
    const node = new javascript.Parser('[\\c]').__consume__charset();
    expect(node.state.warnings).toEqual(['The character set "[\\c]" contains the \\c escape followed by a character other than A-Z. This can lead to different behavior depending on browser. The representation here is the most common interpretation.']);
  });

  describe('_anchor property', function() {

    it('calculates the anchor based on the partContainer', function() {
      const node = new javascript.Parser('[a]').__consume__charset();

      node.partContainer = jasmine.createSpyObj('partContainer', ['getBBox']);
      node.partContainer.getBBox.and.returnValue({
        cy: 20,
      });

      spyOn(node, 'transform').and.returnValue({
        localMatrix: Snap.matrix().translate(3, 8),
      });

      expect(node._anchor).toEqual({
        ay: 28,
      });
    });

  });

  describe('#_render', function() {

    beforeEach(function() {
      let counter = 0;

      this.node = new javascript.Parser('[a]').__consume__charset();
      this.node.label = 'example label';
      this.node.elements = [
        jasmine.createSpyObj('item', ['render']),
        jasmine.createSpyObj('item', ['render']),
        jasmine.createSpyObj('item', ['render']),
      ];
      this.elementDeferred = [
        this.testablePromise(),
        this.testablePromise(),
        this.testablePromise(),
      ];
      this.node.elements[0].render.and.returnValue(this.elementDeferred[0].promise);
      this.node.elements[1].render.and.returnValue(this.elementDeferred[1].promise);
      this.node.elements[2].render.and.returnValue(this.elementDeferred[2].promise);

      this.elementDeferred[0].resolve();
      this.elementDeferred[1].resolve();
      this.elementDeferred[2].resolve();

      this.node.container = Snap(document.createElement('svg')).group();
      this.partContainer = this.node.container.group();
      spyOn(this.node.container, 'group').and.returnValue(this.partContainer);
      spyOn(this.partContainer, 'group').and.callFake(function() {
        return `group ${counter++}`;
      });

      spyOn(this.node, 'renderLabeledBox').and.returnValue('labeled box promise');
      spyOn(util, 'spaceVertically');
    });

    it('creates a cotainer for the parts of the charset', async function() {
      await this.node._render();
      expect(this.node.partContainer).toEqual(this.partContainer);
    });

    it('renders each item', async function() {
      await this.node._render();
      expect(this.node.elements[0].render).toHaveBeenCalledWith('group 0');
      expect(this.node.elements[1].render).toHaveBeenCalledWith('group 1');
      expect(this.node.elements[2].render).toHaveBeenCalledWith('group 2');
    });

    describe('positioning of the items', function() {

      it('spaces the elements vertically', async function() {
        await this.node._render();

        expect(util.spaceVertically).toHaveBeenCalledWith(this.node.elements, { padding: 5 });
      });

      it('renders a labeled box', async function() {
        const result = await this.node._render();

        expect(this.node.renderLabeledBox).toHaveBeenCalledWith('example label', this.partContainer, { padding: 5 });
        expect(result).toEqual('labeled box promise');
      });

    });

  });

});
