define([
  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
  'underscore.string', 'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone',
  'backgrid', 'backform', 'sources/utils',
  'pgadmin.backform', 'pgadmin.backgrid',
  'pgadmin.browser.node.ui',
], function(
  gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
  Backform, commonUtils
) {

  pgAdmin = pgAdmin || window.pgAdmin || {};

  var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};

  // Return back, this has been called more than once
  if (pgAdmin.Tools.maintenance)
    return pgAdmin.Tools.maintenance;

  Backform.CustomSwitchControl = Backform.SwitchControl.extend({
    template: _.template([
      '<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
      '<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
      '  <div class="checkbox">',
      '    <label>',
      '      <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
      '    </label>',
      '  </div>',
      '</div>',
      '<% if (helpMessage && helpMessage.length) { %>',
      '  <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
      '<% } %>',
    ].join('\n')),
    className: 'pgadmin-control-group form-group col-xs-6',
  });

  // Main model for Maintenance functionality
  var MaintenanceModel = Backbone.Model.extend({
    defaults: {
      op: 'VACUUM',
      vacuum_full: false,
      vacuum_freeze: false,
      vacuum_analyze: false,
      verbose: true,
    },
    initialize: function() {
      var node_info = arguments[1]['node_info'];
      // If node is Unique or Primary key then set op to reindex
      if ('primary_key' in node_info || 'unique_constraint' in node_info ||
        'index' in node_info) {
        this.set('op', 'REINDEX');
        this.set('verbose', false);
      }
    },
    schema: [{
      id: 'op',
      label: gettext('Maintenance operation'),
      cell: 'string',
      type: 'text',
      group: gettext('Options'),
      options: [{
        'label': 'VACUUM',
        'value': 'VACUUM',
      },
      {
        'label': 'ANALYZE',
        'value': 'ANALYZE',
      },
      {
        'label': 'REINDEX',
        'value': 'REINDEX',
      },
      {
        'label': 'CLUSTER',
        'value': 'CLUSTER',
      },
      ],
      control: Backform.RadioControl.extend({
        template: _.template([
          '<label class="control-label col-sm-4 col-xs-12"><%=label%></label>',
          '<div class="pgadmin-controls col-xs-12 col-sm-8 btn-group pg-maintenance-op" data-toggle="buttons">',
          ' <% for (var i=0; i < options.length; i++) { %>',
          ' <% var option = options[i]; %>',
          ' <label class="btn btn-primary<% if (i == 0) { %> active<%}%>">',
          '  <input type="radio" name="op" id="op" autocomplete="off" value=<%-formatter.fromRaw(option.value)%><% if (i == 0) { %> selected<%}%> > <%-option.label%>',
          ' </label>',
          ' <% } %>',
          '</div>',
        ].join('\n')),
      }),
      select2: {
        allowClear: false,
        width: '100%',
        placeholder: gettext('Select from list...'),
      },
    },
    {
      type: 'nested',
      control: 'fieldset',
      label: gettext('Vacuum'),
      group: gettext('Options'),
      schema: [{
        id: 'vacuum_full',
        group: gettext('Vacuum'),
        disabled: 'isDisabled',
        control: Backform.CustomSwitchControl,
        label: gettext('FULL'),
        deps: ['op'],
      }, {
        id: 'vacuum_freeze',
        deps: ['op'],
        disabled: 'isDisabled',
        control: Backform.CustomSwitchControl,
        label: gettext('FREEZE'),
        group: gettext('Vacuum'),
      }, {
        id: 'vacuum_analyze',
        deps: ['op'],
        disabled: 'isDisabled',
        control: Backform.CustomSwitchControl,
        label: gettext('ANALYZE'),
        group: gettext('Vacuum'),
      }],
    },
    {
      id: 'verbose',
      group: gettext('Options'),
      deps: ['op'],
      control: Backform.CustomSwitchControl,
      label: gettext('Verbose Messages'),
      disabled: 'isDisabled',
    },
    ],

    // Enable/Disable the items based on the user maintenance operation
    // selection.
    isDisabled: function(m) {
      var node_info = this.node_info;

      switch (this.name) {
      case 'vacuum_full':
      case 'vacuum_freeze':
      case 'vacuum_analyze':
        return (m.get('op') != 'VACUUM');
      case 'verbose':
        if ('primary_key' in node_info || 'unique_constraint' in node_info ||
            'index' in node_info) {
          if (m.get('op') == 'REINDEX') {
            setTimeout(function() {
              m.set('verbose', false);
            }, 10);
            return true;
          }
        }
        return m.get('op') == 'REINDEX';
      default:
        return false;
      }
    },
  });

  pgTools.maintenance = {
    init: function() {

      // We do not want to initialize the module multiple times.
      if (this.initialized)
        return;

      this.initialized = true;

      var maintenance_supported_nodes = [
        'database', 'table', 'primary_key',
        'unique_constraint', 'index', 'partition',
      ];

      /**
       Enable/disable Maintenance menu in tools based on node selected.
       Maintenance menu will be enabled only when user select table and database node.
      */
      var menu_enabled = function(itemData, item) {
        var t = pgBrowser.tree,
          i = item,
          d = itemData;
        var parent_item = t.hasParent(i) ? t.parent(i) : null,
          parent_data = parent_item ? t.itemData(parent_item) : null;
        if (!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
          if (_.indexOf(maintenance_supported_nodes, d._type) !== -1 &&
            parent_data._type != 'catalog') {
            if (d._type == 'database' && d.allowConn)
              return true;
            else if (d._type != 'database')
              return true;
            else
              return false;
          } else
            return false;
        } else
          return false;
      };

      var menus = [{
        name: 'maintenance',
        module: this,
        applies: ['tools'],
        callback: 'callback_maintenance',
        priority: 10,
        label: gettext('Maintenance...'),
        icon: 'fa fa-wrench',
        enable: menu_enabled,
      }];

      // Add supported menus into the menus list
      for (var idx = 0; idx < maintenance_supported_nodes.length; idx++) {
        menus.push({
          name: 'maintenance_context_' + maintenance_supported_nodes[idx],
          node: maintenance_supported_nodes[idx],
          module: this,
          applies: ['context'],
          callback: 'callback_maintenance',
          priority: 10,
          label: gettext('Maintenance...'),
          icon: 'fa fa-wrench',
          enable: menu_enabled,
        });
      }
      pgBrowser.add_menus(menus);
    },

    /*
      Open the dialog for the maintenance functionality
    */
    callback_maintenance: function(args, item) {
      var i = item || pgBrowser.tree.selected(),
        server_data = null;

      while (i) {
        var node_data = pgBrowser.tree.itemData(i);
        if (node_data._type == 'server') {
          server_data = node_data;
          break;
        }

        if (pgBrowser.tree.hasParent(i)) {
          i = $(pgBrowser.tree.parent(i));
        } else {
          Alertify.alert(gettext('Please select server or child node from tree.'));
          break;
        }
      }

      if (!server_data) {
        return;
      }

      var module = 'paths',
        preference_name = 'pg_bin_dir',
        msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');

      if ((server_data.type && server_data.type == 'ppas') ||
        server_data.server_type == 'ppas') {
        preference_name = 'ppas_bin_dir';
        msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
      }

      var preference = pgBrowser.get_preference(module, preference_name);

      if (preference) {
        if (!preference.value) {
          Alertify.alert(gettext('Configuration required'), msg);
          return;
        }
      } else {
        Alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
        return;
      }

      var self = this,
        t = pgBrowser.tree;

      i = item || t.selected();

      var d = i && i.length == 1 ? t.itemData(i) : undefined;

      if (!d)
        return;

      if (!Alertify.MaintenanceDialog) {
        Alertify.dialog('MaintenanceDialog', function factory() {

          return {
            main: function(title) {
              this.set('title', title);
            },
            setup: function() {
              return {
                buttons: [{
                  text: '',
                  className: 'btn btn-default pull-left fa fa-lg fa-info',
                  attrs: {
                    name: 'object_help',
                    type: 'button',
                    url: 'maintenance.html',
                    label: gettext('Maintenance'),
                  },
                }, {
                  text: '',
                  key: 112,
                  className: 'btn btn-default pull-left fa fa-lg fa-question',
                  attrs: {
                    name: 'dialog_help',
                    type: 'button',
                    label: gettext('Maintenance'),
                    url: url_for(
                      'help.static', {
                        'filename': 'maintenance_dialog.html',
                      }
                    ),
                  },
                }, {
                  text: gettext('OK'),
                  key: 13,
                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
                  'data-btn-name': 'ok',
                }, {
                  text: gettext('Cancel'),
                  key: 27,
                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
                  'data-btn-name': 'cancel',
                }],
                options: {
                  modal: 0,
                  pinnable: false,
                },
              };
            },
            // Callback functions when click on the buttons of the Alertify dialogs
            callback: function(e) {
              var i = pgBrowser.tree.selected(),
                d = i && i.length == 1 ? pgBrowser.tree.itemData(i) : undefined,
                node = d && pgBrowser.Nodes[d._type];

              if (e.button.element.name == 'dialog_help' || e.button.element.name == 'object_help') {
                e.cancel = true;
                pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
                  node, i, e.button.element.getAttribute('label'));
                return;
              }

              if (e.button['data-btn-name'] === 'ok') {

                var schema = undefined,
                  table = undefined,
                  primary_key = undefined,
                  unique_constraint = undefined,
                  index = undefined;

                if (!d)
                  return;

                var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);

                if (treeInfo.schema != undefined) {
                  schema = treeInfo.schema._label;
                }

                if (treeInfo.partition != undefined) {
                  table = treeInfo.partition._label;
                } else if (treeInfo.table != undefined) {
                  table = treeInfo.table._label;
                }

                if (treeInfo.primary_key != undefined) {
                  primary_key = treeInfo.primary_key._label;
                } else if (treeInfo.unique_constraint != undefined) {
                  unique_constraint = treeInfo.unique_constraint._label;
                } else if (treeInfo.index != undefined) {
                  index = treeInfo.index._label;
                }

                this.view.model.set({
                  'database': treeInfo.database._label,
                  'schema': schema,
                  'table': table,
                  'primary_key': primary_key,
                  'unique_constraint': unique_constraint,
                  'index': index,
                });

                $.ajax({
                  url: url_for(
                    'maintenance.create_job', {
                      'sid': treeInfo.server._id,
                      'did': treeInfo.database._id,
                    }),
                  method: 'POST',
                  data: {
                    'data': JSON.stringify(this.view.model.toJSON()),
                  },
                  success: function(res) {
                    if (res.data && res.data.status) {
                      //Do nothing as we are creating the job and exiting from the main dialog
                      Alertify.success(res.data.info);
                      pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
                    } else {
                      Alertify.error(res.data.errmsg);
                    }
                  },
                  error: function() {
                    Alertify.alert(
                      gettext('Maintenance job creation failed.')
                    );
                  },
                });
              }
            },
            build: function() {
              Alertify.pgDialogBuild.apply(this);
            },
            hooks: {
              onclose: function() {
                if (this.view) {
                  this.view.remove({
                    data: true,
                    internal: true,
                    silent: true,
                  });
                }
              },
            },
            prepare: function() {
              // Main maintenance tool dialog container
              var $container = $('<div class=\'maintenance_dlg\'></div>');

              var t = pgBrowser.tree,
                i = t.selected(),
                d = i && i.length == 1 ? t.itemData(i) : undefined,
                node = d && pgBrowser.Nodes[d._type];

              if (!d)
                return;

              var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);

              var newModel = new MaintenanceModel({}, {
                  node_info: treeInfo,
                }),
                fields = Backform.generateViewSchema(
                  treeInfo, newModel, 'create', node, treeInfo.server, true
                );

              var view = this.view = new Backform.Dialog({
                el: $container,
                model: newModel,
                schema: fields,
              });

              $(this.elements.body.childNodes[0]).addClass('alertify_tools_dialog_properties obj_properties');
              view.render();

              // If node is Index, Unique or Primary key then disable vacuum & analyze button
              if (d._type == 'primary_key' || d._type == 'unique_constraint' ||
                d._type == 'index') {
                var vacuum_analyze_btns = $container.find(
                  '.pgadmin-controls label:lt(2)'
                ).removeClass('active').addClass('disabled');
                // Find reindex button element & add active class to it
                var reindex_btn = vacuum_analyze_btns[1].nextElementSibling;
                $(reindex_btn).addClass('active');
              }

              view.$el.attr('tabindex', -1);
              var container = view.$el.find('.tab-content:first > .tab-pane.active:first');
              commonUtils.findAndSetFocus(container);

              this.elements.content.appendChild($container.get(0));
            },
          };
        });
      }

      // Open the Alertify dialog
      Alertify.MaintenanceDialog('Maintenance...').set('resizable', true).resizeTo('60%', '80%');
    },
  };

  return pgAdmin.Tools.maintenance;
});
