'use babel';

/** @jsx etch.dom */

import { CompositeDisposable } from 'atom';
import MenuEditorPanel from './menu-editor-panel';
import path from 'path';
import fs from 'fs';
import _ from 'underscore-plus';

export default {

  panelName: 'Menu',
  subscriptions: null,

  /**
   * Stores the configuration loaded from the external file
   * @type {Object}
   */
  configuration: {
    main: {},
    context: {}
  },
  /**
   * Stores a deep copy of the menus to be kept as a reference
   * @type {Object}
   */
  menu: {},

  /**
   * Default atom api activate call
   * @param  {Object} state serialized state
   */
  activate(state) {
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();

    // Wait for every package to have loaded, so that the menus are populated
    this.subscriptions.add(atom.packages.onDidActivateInitialPackages(() => {
      // Create a clone of the two menus, so that the settings view can be populated
      // with the correct items
      this.menu = {
        main: _.deepClone(atom.menu.template),
        context: _.deepClone(atom.contextMenu.itemSets)
      };

      // load the previous settings from the config file
      this.loadConfiguration();

      // Inject the new panel in settings-view when we open the settings
      this.subscriptions.add(atom.workspace.observeActivePaneItem((item) => {
        // Avoid any possible undefined errors
        if (item && item.uri && item.uri == 'atom://config') {
          // TODO: maybe we can use panelCreateCallbacks[this.panelName] to check for existence
          if (!item._menuEditorPanelAdded) {
            item.addCorePanel(this.panelName, 'list-unordered', () => new MenuEditorPanel(this.configuration, this.menu, () => this.onDidChangeConfig()));
            // A variable that confirms that we've already added a panel
            // to the settings panel. see before
            item._menuEditorPanelAdded = true;
          }
        }
      }));
    }));
  },

  /**
   * Update atom menus depending on the configuration
   */
  updateMenus() {
    atom.menu.clear();
    atom.menu.loadPlatformItems();
    this.updateMainMenus(this.configuration.main, atom.menu.template);
  },

  /**
   * Update recusively the main menu, reading from the config
   * @param  {Object} config the configuration relative to @menu
   * @param  {Object} menu   the menu subset
   */
  updateMainMenus(config, menu) {
    let length = menu.length;

    // Cycle trough all the top menu elements
    for (let i = 0; i < length; i++) {
      let item = menu[i];

      // If it has a submenu just call @updateMainMenus recusively on it
      if (item.submenu) {
        this.updateMainMenus(config[item.label].children, item.submenu);
      // Otherwise it is a menu entry
      } else {
        // See if a config setting is available for that entry
        // if not just skip the entry (aka keep it in the menu)
        if (!config[item.label]) {
          continue;
        }
        // if the config setting is present and it is not enabled, just delete
        // that entry
        if (!config[item.label].enabled) {
          // atom.MenuManager internal call, basically it removes the @item
          // from the @menu
          atom.menu.unmerge(menu, item);
          // IMPORTANT: menu array has shortened, we need to pull back the
          // counter and the length
          i--;
          length--;
        }
      }
    }

    // Update menu. Necessary to show the changes
    atom.menu.update();
  },

  /**
   * Callback for when a checkbox in the settings view changes its value
   */
  onDidChangeConfig() {
    // Save configuration to a file
    this.saveConfiguration();
    // The configuration has just changed, we need to update the menus
    this.updateMenus();
  },

  /**
   * Return default config file path (in atom config path directory, usually
   * "~/.atom")
   * @return {String} config file path
   */
  getConfigPath() {
    return path.join(atom.getConfigDirPath(), 'menu-config.json');
  },

  /**
   * Loads configuration from the default config file and saves it to the
   * @this.configuration object. if it doesn't exist just create it with an
   * empty configuration.
   */
  loadConfiguration() {
    // check for config file existence
    if (fs.existsSync(this.getConfigPath())) {
      // load it into the configuration object and update the menus
      this.configuration = JSON.parse(fs.readFileSync(this.getConfigPath(), 'utf8'));
      this.updateMenus();
    } else {
      // save an empty configuration. see @this.configuration
      this.saveConfiguration();
    }
  },

  /**
   * Save the @this.configuration to the default file
   */
  saveConfiguration() {
    // Just stringify the object
    fs.writeFileSync(this.getConfigPath(), JSON.stringify(this.configuration));
  },

  /**
   * Default atom api deactivate call
   */
  deactivate() {
    this.subscriptions.dispose();
  }

};

@@ -2266,16 +2266,165 @@
 mplate);
+%0A%0A    atom.contextMenu.itemSets = _.deepClone(this.menu.context);%0A    this.updateContextMenus(this.configuration.context, atom.contextMenu.itemSets);
 %0A  %7D,%0A%0A 
@@ -3732,32 +3732,1098 @@
 update();%0A  %7D,%0A%0A
+  updateContextMenus(config, menu) %7B%0A    let length = menu.length;%0A%0A    for (let i = 0; i %3C length; i++) %7B%0A      let item = menu%5Bi%5D;%0A      let label = item.label %7C%7C item.selector;%0A%0A      // If it has a submenu just call @updateMainMenus recusively on it%0A      if (item.items) %7B%0A        this.updateMainMenus(config%5Blabel%5D.children, item.items);%0A      // Otherwise it is a menu entry%0A      %7D else %7B%0A        // See if a config setting is available for that entry%0A        // if not just skip the entry (aka keep it in the menu)%0A        if (!config%5Bitem.label%5D) %7B%0A          continue;%0A        %7D%0A        // if the config setting is present and it is not enabled, just delete%0A        // that entry%0A        if (!config%5Bitem.label%5D.enabled) %7B%0A          // atom.MenuManager internal call, basically it removes the @item%0A          // from the @menu%0A          atom.contextMenu.itemSets.splice(item, 1);%0A          // IMPORTANT: menu array has shortened, we need to pull back the%0A          // counter and the length%0A          i--;%0A          length--;%0A        %7D%0A      %7D%0A    %7D%0A  %7D,%0A%0A
   /**%0A   * Callb

Implement basic context menu editing