import { defined } from "../../../Source/Cesium.js";
import { TimeInterval } from "../../../Source/Cesium.js";
import createViewer from "../../createViewer.js";
import DomEventSimulator from "../../DomEventSimulator.js";
import pollToPromise from "../../pollToPromise.js";
import { viewerDragDropMixin } from "../../../Source/Cesium.js";

describe(
  "Widgets/Viewer/viewerDragDropMixin",
  function () {
    var container;
    var viewer;
    beforeEach(function () {
      container = document.createElement("div");
      container.id = "container";
      container.style.display = "none";
      document.body.appendChild(container);

      //Impersonate FileReader for drag and drop tests
      var fakeFileReader = jasmine.createSpyObj("FileReader", ["readAsText"]);
      fakeFileReader.readAsText = function (file) {
        if (defined(file.czmlString)) {
          this.onload({
            target: {
              result: file.czmlString,
            },
          });
        } else {
          this.onerror({
            target: {
              error: file.errorMessage,
            },
          });
        }
      };
      spyOn(window, "FileReader").and.returnValue(fakeFileReader);
    });

    afterEach(function () {
      if (viewer && !viewer.isDestroyed()) {
        viewer = viewer.destroy();
      }

      document.body.removeChild(container);
    });

    it("mixin sets default values", function () {
      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);
      expect(viewer.dropTarget).toBe(viewer.container);
      expect(viewer.dropEnabled).toEqual(true);
      expect(viewer.clearOnDrop).toEqual(true);
      expect(viewer.clampToGround).toEqual(true);
      expect(viewer.flyToOnDrop).toEqual(true);
    });

    it("clearOnDrop defaults to true when dataSourceBrowser is not used", function () {
      viewer = createViewer(container, {
        dataSourceBrowser: false,
      });
      viewer.extend(viewerDragDropMixin);
      expect(viewer.clearOnDrop).toEqual(true);
    });

    it("mixin sets option values", function () {
      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin, {
        dropTarget: document.body,
        clearOnDrop: false,
        clampToGround: false,
        flyToOnDrop: false,
      });
      expect(viewer.dropTarget).toBe(document.body);
      expect(viewer.dropEnabled).toEqual(true);
      expect(viewer.clearOnDrop).toEqual(false);
      expect(viewer.clampToGround).toEqual(false);
      expect(viewer.flyToOnDrop).toEqual(false);
    });

    it("mixin works with dropTarget id string", function () {
      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin, {
        dropTarget: "container",
      });
      expect(viewer.dropTarget).toBe(container);
    });

    var czml1 = [
      {
        id: "document",
        version: "1.0",
        clock: {
          interval: "2000-01-01/2001-01-01",
        },
      },
      {
        id: "test",
      },
    ];

    var czml2 = [
      {
        id: "document",
        version: "1.0",
        clock: {
          interval: "2000-01-02/2001-01-02",
        },
      },
      {
        id: "test2",
      },
    ];

    it("handleDrop processes drop event", function () {
      var mockEvent = {
        dataTransfer: {
          files: [
            {
              name: "czml1.czml",
              czmlString: JSON.stringify(czml1),
            },
          ],
        },
        stopPropagation: function () {},
        preventDefault: function () {},
      };

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);

      DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

      return pollToPromise(function () {
        return viewer.dataSources.length === 1;
      }).then(function () {
        var dataSource = viewer.dataSources.get(0);
        var interval = TimeInterval.fromIso8601({
          iso8601: czml1[0].clock.interval,
        });
        expect(dataSource.entities.getById("test")).toBeDefined();
        expect(dataSource.clock.startTime).toEqual(interval.start);
        expect(dataSource.clock.stopTime).toEqual(interval.stop);
      });
    });

    it("handleDrop processes drop event with multiple files", function () {
      var mockEvent = {
        dataTransfer: {
          files: [
            {
              name: "czml1.czml",
              czmlString: JSON.stringify(czml1),
            },
            {
              name: "czml2.czml",
              czmlString: JSON.stringify(czml2),
            },
          ],
        },
        stopPropagation: function () {},
        preventDefault: function () {},
      };

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);

      DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

      return pollToPromise(function () {
        return viewer.dataSources.length === 2;
      }).then(function () {
        var source1 = viewer.dataSources.get(0);
        var source2 = viewer.dataSources.get(1);
        expect(source1.entities.getById("test")).toBeDefined();
        expect(source2.entities.getById("test2")).toBeDefined();
        //Interval of first file should be used.
        var interval = TimeInterval.fromIso8601({
          iso8601: czml1[0].clock.interval,
        });
        expect(source1.clock.startTime).toEqual(interval.start);
        expect(source1.clock.stopTime).toEqual(interval.stop);
      });
    });

    it("handleDrop obeys clearOnDrop", function () {
      var mockEvent = {
        dataTransfer: {
          files: [
            {
              name: "czml1.czml",
              czmlString: JSON.stringify(czml1),
            },
            {
              name: "czml2.czml",
              czmlString: JSON.stringify(czml2),
            },
          ],
        },
        stopPropagation: function () {},
        preventDefault: function () {},
      };

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);

      DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

      return pollToPromise(function () {
        return viewer.dataSources.length === 2;
      }).then(function () {
        var source1 = viewer.dataSources.get(0);
        var source2 = viewer.dataSources.get(1);
        expect(source1.entities.getById("test")).toBeDefined();
        expect(source2.entities.getById("test2")).toBeDefined();
        //Interval of first file should be used.
        var interval = TimeInterval.fromIso8601({
          iso8601: czml1[0].clock.interval,
        });
        expect(source1.clock.startTime).toEqual(interval.start);
        expect(source1.clock.stopTime).toEqual(interval.stop);

        viewer.clearOnDrop = false;
        DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

        return pollToPromise(function () {
          return viewer.dataSources.length === 4;
        }).then(function () {
          var source1 = viewer.dataSources.get(0);
          var source2 = viewer.dataSources.get(1);
          var source3 = viewer.dataSources.get(2);
          var source4 = viewer.dataSources.get(3);

          expect(source1.entities.getById("test")).toBeDefined();
          expect(source2.entities.getById("test2")).toBeDefined();
          expect(source3.entities.getById("test")).toBeDefined();
          expect(source4.entities.getById("test2")).toBeDefined();

          viewer.clearOnDrop = true;
          DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

          return pollToPromise(function () {
            return viewer.dataSources.length === 2;
          }).then(function () {
            var source1 = viewer.dataSources.get(0);
            var source2 = viewer.dataSources.get(1);
            expect(source1.entities.getById("test")).toBeDefined();
            expect(source2.entities.getById("test2")).toBeDefined();
            //Interval of first file should be used.
            var interval = TimeInterval.fromIso8601({
              iso8601: czml1[0].clock.interval,
            });
            expect(source1.clock.startTime).toEqual(interval.start);
            expect(source1.clock.stopTime).toEqual(interval.stop);
          });
        });
      });
    });

    it("dropError is raised on exception", function () {
      var mockEvent = {
        dataTransfer: {
          files: [
            {
              name: "czml1.czml",
              czmlString: "bad JSON",
            },
          ],
        },
        stopPropagation: function () {},
        preventDefault: function () {},
      };

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);

      var spyListener = jasmine.createSpy("listener");

      viewer.dropError.addEventListener(spyListener);
      DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

      return pollToPromise(function () {
        return spyListener.calls.any();
      }).then(function () {
        expect(spyListener).toHaveBeenCalledWith(
          viewer,
          "czml1.czml",
          jasmine.any(SyntaxError)
        );

        viewer.dropError.removeEventListener(spyListener);
      });
    });

    it("dropError is raised FileReader error", function () {
      var mockEvent = {
        dataTransfer: {
          files: [
            {
              name: "czml1.czml",
              errorMessage: "bad JSON",
            },
          ],
        },
        stopPropagation: function () {},
        preventDefault: function () {},
      };

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);

      var spyListener = jasmine.createSpy("listener");

      viewer.dropError.addEventListener(spyListener);
      DomEventSimulator.fireMockEvent(viewer._handleDrop, mockEvent);

      return pollToPromise(function () {
        return spyListener.calls.any();
      }).then(function () {
        expect(spyListener).toHaveBeenCalledWith(
          viewer,
          mockEvent.dataTransfer.files[0].name,
          mockEvent.dataTransfer.files[0].errorMessage
        );

        viewer.dropError.removeEventListener(spyListener);
      });
    });

    function MockContainer() {
      var events = {};
      this.events = events;

      this.addEventListener = function (name, func, bubble) {
        events[name] = {
          func: func,
          bubble: bubble,
        };
      };

      this.removeEventListener = function (name, func, bubble) {
        var subscribed = events[name];
        expect(subscribed.func).toBe(func);
        expect(subscribed.bubble).toEqual(bubble);
        delete events[name];
      };
    }

    it("enable/disable subscribes to provided dropTarget.", function () {
      var dropTarget = new MockContainer();

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin, {
        dropTarget: dropTarget,
      });

      expect(dropTarget.events.drop).toBeDefined();
      expect(dropTarget.events.dragenter).toBeDefined();
      expect(dropTarget.events.dragover).toBeDefined();
      expect(dropTarget.events.dragexit).toBeDefined();

      viewer.dropEnabled = false;
      expect(dropTarget.events.drop).toBeUndefined();
      expect(dropTarget.events.dragenter).toBeUndefined();
      expect(dropTarget.events.dragover).toBeUndefined();
      expect(dropTarget.events.dragexit).toBeUndefined();

      viewer.dropEnabled = true;
      expect(dropTarget.events.drop).toBeDefined();
      expect(dropTarget.events.dragenter).toBeDefined();
      expect(dropTarget.events.dragover).toBeDefined();
      expect(dropTarget.events.dragexit).toBeDefined();
    });

    it("can set new dropTarget.", function () {
      var dropTarget1 = new MockContainer();
      var dropTarget2 = new MockContainer();

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin, {
        dropTarget: dropTarget1,
      });

      expect(dropTarget1.events.drop).toBeDefined();
      expect(dropTarget1.events.dragenter).toBeDefined();
      expect(dropTarget1.events.dragover).toBeDefined();
      expect(dropTarget1.events.dragexit).toBeDefined();

      viewer.dropTarget = dropTarget2;
      expect(dropTarget1.events.drop).toBeUndefined();
      expect(dropTarget1.events.dragenter).toBeUndefined();
      expect(dropTarget1.events.dragover).toBeUndefined();
      expect(dropTarget1.events.dragexit).toBeUndefined();

      expect(dropTarget2.events.drop).toBeDefined();
      expect(dropTarget2.events.dragenter).toBeDefined();
      expect(dropTarget2.events.dragover).toBeDefined();
      expect(dropTarget2.events.dragexit).toBeDefined();
    });

    it("can set proxy.", function () {
      var proxy = {};

      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin, {
        proxy: proxy,
      });
      expect(viewer.proxy).toBe(proxy);
    });

    it("throws with undefined viewer", function () {
      expect(function () {
        viewerDragDropMixin(undefined);
      }).toThrowDeveloperError();
    });

    it("throws with non-existant string container", function () {
      viewer = createViewer(container);
      expect(function () {
        viewer.extend(viewerDragDropMixin, {
          dropTarget: "doesNotExist",
        });
      }).toThrowDeveloperError();
    });

    it("throws if dropTarget property already added by another mixin.", function () {
      viewer = createViewer(container);
      viewer.dropTarget = true;
      expect(function () {
        viewer.extend(viewerDragDropMixin);
      }).toThrowDeveloperError();
    });

    it("throws if dropEnabled property already added by another mixin.", function () {
      viewer = createViewer(container);
      viewer.dropEnabled = true;
      expect(function () {
        viewer.extend(viewerDragDropMixin);
      }).toThrowDeveloperError();
    });

    it("throws if dropError property already added by another mixin.", function () {
      viewer = createViewer(container);
      viewer.dropError = true;
      expect(function () {
        viewer.extend(viewerDragDropMixin);
      }).toThrowDeveloperError();
    });

    it("throws if clearOnDrop property already added by another mixin.", function () {
      viewer = createViewer(container);
      viewer.clearOnDrop = true;
      expect(function () {
        viewer.extend(viewerDragDropMixin);
      }).toThrowDeveloperError();
    });

    it("throws if flyToOnDrop property already added by another mixin.", function () {
      viewer = createViewer(container);
      viewer.flyToOnDrop = true;
      expect(function () {
        viewer.extend(viewerDragDropMixin);
      }).toThrowDeveloperError();
    });

    it("setting dropTarget to undefined throws exception", function () {
      viewer = createViewer(container);
      viewer.extend(viewerDragDropMixin);
      expect(function () {
        viewer.dropTarget = undefined;
      }).toThrowDeveloperError();
    });
  },
  "WebGL"
);
