'use babel';

// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
import { CompositeDisposable } from 'atom';
import MinimapLinterBinding from './minimap-linter-binding';

const messagePath = message =>
  (message.version === 1 ? message.filePath : message.location.file);
const messageRange = message =>
  (message.version === 1 ? message.range : message.location.position);
const goodMessage = (message, filePath) =>
  (messagePath(message) === filePath && messageRange(message));

export default {
  // Atom package lifecycle events start
  activate() {
    this.bindings = new Map();
    this.idleCallbacks = new Set();
    let depsCallbackID;
    const installMinimapLinterDeps = () => {
      this.idleCallbacks.delete(depsCallbackID);
      if (!atom.inSpecMode()) {
        require('atom-package-deps').install('minimap-linter');
      }
    };
    depsCallbackID = window.requestIdleCallback(installMinimapLinterDeps);
    this.idleCallbacks.add(depsCallbackID);

    this.subscriptions = new CompositeDisposable();
    this.messageCache = new Set();
  },

  deactivate() {
    this.idleCallbacks.forEach(callbackID => window.cancelIdleCallback(callbackID));
    this.idleCallbacks.clear();
    if (!this.minimapProvider) {
      return;
    }
    this.minimapProvider.unregisterPlugin('linter');
    this.minimapProvider = null;
    this.messageCache.clear();
  },
  // Atom package lifecycle events end

  // Package dependencies provisioning start
  // Minimap
  consumeMinimapServiceV1(minimap) {
    this.minimapProvider = minimap;
    this.minimapProvider.registerPlugin('linter', this);
  },

  // LinterUI (for messages)
  provideUI() {
    const { bindings: minimapBindings, messageCache } = this;
    return {
      name: 'minimap-linter',
      render(messagePatch) {
        // Parse out what messages have been added/removed
        const added = new Set();
        const removed = new Set();
        minimapBindings.forEach((binding, minimap) => {
          const textEditor = minimap.getTextEditor();
          if (!atom.workspace.isTextEditor(textEditor)) {
            return;
          }
          const filePath = textEditor.getPath();
          messagePatch.added.forEach((message) => {
            if (goodMessage(message, filePath)) {
              added.add(message);
              binding.addMessage(message);
            }
          });
          messagePatch.removed.forEach((message) => {
            removed.add(message);
            if (binding.hasMessage(message)) {
              binding.removeMessage(message);
            }
          });
        });
        added.forEach(message => messageCache.add(message));
        removed.forEach(message => messageCache.delete(message));
      },
      didBeginLinting() {},
      didFinishLinting() {},
      dispose() {
        minimapBindings.forEach(binding => binding.removeMessages());
      },
    };
  },
  // Package dependencies provisioning end

  // Minimap plugin lifecycle events start
  active: false,

  isActive() {
    return this.minimapsSubscription !== undefined && !this.minimapsSubscription.disposed;
  },

  activatePlugin() {
    if (this.active) {
      return;
    }

    this.active = true;

    // Handle each minimap
    this.minimapsSubscription = this.minimapProvider.observeMinimaps((minimap) => {
      const binding = new MinimapLinterBinding(minimap);
      this.bindings.set(minimap, binding);
      const subscription = minimap.onDidDestroy(() => {
        binding.destroy();
        this.subscriptions.remove(subscription);
        subscription.dispose();
        this.bindings.delete(minimap);
      });
      this.subscriptions.add(subscription);

      // Force rendering of old messages after a small delay
      let oldMsgCallbackID;
      const renderOldMessages = () => {
        this.idleCallbacks.delete(oldMsgCallbackID);
        const textEditor = minimap.getTextEditor();
        if (!atom.workspace.isTextEditor(textEditor)) {
          return;
        }
        const filePath = textEditor.getPath();
        this.messageCache.forEach((message) => {
          if (goodMessage(message, filePath)) {
            binding.addMessage(message);
          }
        });
      };
      oldMsgCallbackID = window.requestIdleCallback(renderOldMessages);
      this.idleCallbacks.add(oldMsgCallbackID);
    });
  },

  deactivatePlugin() {
    if (!this.active) {
      return;
    }
    this.bindings.forEach((binding, minimap) => {
      binding.destroy();
      this.bindings.delete(minimap);
    });
    this.minimapsSubscription.dispose();
    this.subscriptions.dispose();
  },
  // Minimap plugin lifecycle events end
};

@@ -1482,16 +1482,379 @@
 g start%0A
+  // Message management%0A  updateMessageCache(added, removed) %7B%0A    added.forEach(message =%3E this.messageCache.add(message));%0A    removed.forEach((message) =%3E %7B%0A      this.bindings.forEach((binding) =%3E %7B%0A        if (binding.hasMessage(message)) %7B%0A          binding.removeMessage(message);%0A        %7D%0A      %7D);%0A      this.messageCache.delete(message);%0A    %7D);%0A  %7D,%0A%0A
   // Min
@@ -2035,24 +2035,91 @@
 ovideUI() %7B%0A
+    const updateMessageCache = this.updateMessageCache.bind(this);%0A
     const %7B 
@@ -2147,22 +2147,8 @@
 ings
-, messageCache
  %7D =
@@ -2154,16 +2154,17 @@
 = this;%0A
+%0A
     retu
@@ -2311,32 +2311,96 @@
 ed = new Set();%0A
+        // Store the removed messages for later batched removal%0A
         const re
@@ -2407,35 +2407,56 @@
 moved = new Set(
+messagePatch.removed
 );%0A
+%0A
         minimapB
@@ -2491,24 +2491,57 @@
 nimap) =%3E %7B%0A
+          // Validate the editor%0A
           co
@@ -2713,32 +2713,150 @@
 itor.getPath();%0A
+          if (!filePath) %7B%0A            return;%0A          %7D%0A%0A          // Add the messages specific to this TextEditor%0A
           messag
@@ -3058,255 +3058,28 @@
     
-  messagePatch.removed.forEach((message) =%3E %7B%0A            removed.add(message);%0A            if (binding.hasMessage(message)) %7B%0A              binding.removeMessage(message);%0A            %7D%0A          %7D);%0A        %7D);%0A        added.forEach(message =%3E m
+%7D);%0A%0A        updateM
 essa
@@ -3089,87 +3089,23 @@
 ache
-.add(message));%0A        removed.forEach(message =%3E messageCache.delete(message)
+(added, removed
 );%0A 

Remove messages from all bindings