/**
 * @license
 * Copyright 2019 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

suite("Trashcan", function() {
  function fireDeleteEvent(workspace, xmlString) {
    var xml = Blockly.Xml.textToDom(
        '<xml xmlns="https://developers.google.com/blockly/xml">' +
        xmlString + '</xml>');
    xml = xml.children[0];
    var event = new Blockly.Events.Delete();
    event.oldXml = xml;
    event.workspaceId = workspace.id;
    Blockly.Events.fire(event);
  }
  function fireNonDeleteEvent(workspace, oldXml) {
    var event = new Blockly.Events.Abstract();
    event.type = 'dummy_type';
    event.workspaceId = workspace.id;
    if (oldXml) {
      event.oldXml = oldXml;
    }
    Blockly.Events.fire(/** @type {Blockly.Events.Abstract} */ event);
  }

  setup(function() {
    sharedTestSetup.call(this);
    this.workspace = Blockly.inject('blocklyDiv',
        {'trashcan': true, 'maxTrashcanContents': Infinity});
    this.trashcan = this.workspace.trashcan;
  });
  teardown(function() {
    sharedTestTeardown.call(this);
  });

  suite("Events", function() {
    test("Delete", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("Non-Delete", function() {
      fireNonDeleteEvent(this.workspace);
      chai.assert.equal(this.trashcan.contents_.length, 0);
    });
    test("Non-Delete w/ oldXml", function() {
      var xml = Blockly.Xml.textToDom(
          '<xml xmlns="https://developers.google.com/blockly/xml">' +
          '  <block type="dummy_type"/>' +
          '</xml>'
      );
      xml = xml.children[0];
      fireNonDeleteEvent(this.workspace, xml);
      chai.assert.equal(this.trashcan.contents_.length, 0);
    });
    test("Shadow Delete", function() {
      fireDeleteEvent(this.workspace, '<shadow type="dummy_type"/>');
      chai.assert.equal(this.trashcan.contents_.length, 0);
    });
    test("Click without contents - fires workspace click", function() {
      simulateClick(this.trashcan.svgGroup_);

      assertEventNotFired(
          this.eventsFireStub, Blockly.Events.TrashcanOpen, {});
      assertEventFired(
          this.eventsFireStub, Blockly.Events.Click, {targetType: 'workspace'},
          this.workspace.id, null);
    });
    test("Click with contents - fires trashcanOpen", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
      // Stub flyout interaction.
      var showFlyoutStub = sinon.stub(this.trashcan.flyout, "show");

      simulateClick(this.trashcan.svgGroup_);

      sinon.assert.calledOnce(showFlyoutStub);

      assertEventFired(
          this.eventsFireStub, Blockly.Events.TrashcanOpen,
          {isOpen: true}, this.workspace.id);
      assertEventNotFired(
          this.eventsFireStub, Blockly.Events.Click, {});
    });
    test("Click outside trashcan - fires trashcanClose", function() {
      sinon.stub(this.trashcan.flyout, 'isVisible').returns(true);
      // Stub flyout interaction.
      var hideFlyoutStub = sinon.stub(this.trashcan.flyout, "hide");

      simulateClick(this.workspace.svgGroup_);

      sinon.assert.calledOnce(hideFlyoutStub);

      assertEventFired(
          this.eventsFireStub, Blockly.Events.TrashcanOpen,
          {isOpen: false}, this.workspace.id);
      assertEventFired(
          this.eventsFireStub, Blockly.Events.Click, {targetType: 'workspace'},
          this.workspace.id, null);
    });
  });
  suite("Unique Contents", function() {
    test("Simple", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("Different Coords", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type" x="10" y="10"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type" x="20" y="20"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("Different IDs", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type" id="id1"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type" id="id2"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("No Disabled - Disabled True", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type" disabled="true"/>');
      // Disabled tags get removed because disabled blocks aren't allowed to
      // be dragged from flyouts. See #2239 and #3243.
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("No Editable - Editable False", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type" editable="false"/>');
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("No Movable - Movable False", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type" movable="false"/>');
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Field Values", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <field name="dummy_name">dummy_value1</field>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <field name="dummy_name">dummy_value2</field>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("No Values - Values", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <value name="dummy_input">' +
          '    <block type="dummy_type"/>' +
          '  </value>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Value Blocks", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <value name="dummy_input">' +
          '    <block type="dummy_type1"/>' +
          '  </value>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <value name="dummy_input">' +
          '    <block type="dummy_type2"/>' +
          '  </value>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("No Statements - Statements", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <statement name="dummy_input">' +
          '    <block type="dummy_type"/>' +
          '  </statement>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Statement Blocks", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <statement name="dummy_input">' +
          '    <block type="dummy_type1"/>' +
          '  </statement>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <statement name="dummy_input">' +
          '    <block type="dummy_type2"/>' +
          '  </statement>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("No Next - Next", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <next>' +
          '    <block type="dummy_type"/>' +
          '  </next>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Next Blocks", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <next>' +
          '    <block type="dummy_type1"/>' +
          '  </next>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <next>' +
          '    <block type="dummy_type2"/>' +
          '  </next>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("No Comment - Comment", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment>comment_text</comment>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Comment Text", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment>comment_text1</comment>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment>comment_text2</comment>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Comment Size", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment h="10" w="10">comment_text</comment>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment h="20" w="20">comment_text</comment>' +
          '</block>'
      );
      // h & w tags are removed b/c the blocks appear the same.
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("Different Comment Pinned", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment pinned="false">comment_text</comment>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <comment pinned="true">comment_text</comment>' +
          '</block>'
      );
      // pinned tags are removed b/c the blocks appear the same.
      chai.assert.equal(this.trashcan.contents_.length, 1);
    });
    test("No Mutator - Mutator", function() {
      fireDeleteEvent(this.workspace, '<block type="dummy_type"/>');
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <mutation dummy_attribute="dummy_value"></mutation>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
    test("Different Mutator", function() {
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <mutation dummy_attribute="dummy_value1"></mutation>' +
          '</block>'
      );
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type">' +
          '  <mutation dummy_attribute="dummy_value2"></mutation>' +
          '</block>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 2);
    });
  });
  suite("Max Contents", function() {
    test("Max 0", function() {
      this.workspace.options.maxTrashcanContents = 0;
      fireDeleteEvent(this.workspace,
          '<block type="dummy_type"/>'
      );
      chai.assert.equal(this.trashcan.contents_.length, 0);
      this.workspace.options.maxTrashcanContents = Infinity;
    });
    test("Last In First Out", function() {
      this.workspace.options.maxTrashcanContents = 1;
      fireDeleteEvent(this.workspace, '<block type="dummy_type1"/>');
      fireDeleteEvent(this.workspace, '<block type="dummy_type2"/>');
      chai.assert.equal(this.trashcan.contents_.length, 1);
      chai.assert.equal(
          Blockly.Xml.textToDom(this.trashcan.contents_[0])
              .getAttribute('type'),
          'dummy_type2'
      );
      this.workspace.options.maxTrashcanContents = Infinity;
    });
  });
});
