define('pgadmin.datagrid', [
  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs',
  'sources/pgadmin', 'bundled_codemirror', 'sources/sqleditor_utils', 'wcdocker'
], function(gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils) {
    // Some scripts do export their object in the window only.
    // Generally the one, which do no have AMD support.
    var wcDocker = window.wcDocker,
      pgBrowser = pgAdmin.Browser,
      CodeMirror = codemirror.default;

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

    pgAdmin.DataGrid = {
      init: function() {
        if (this.initialized)
            return;
        this.initialized = true;
        this.title_index = 1;

        this.spinner_el = '<div class="wcLoadingContainer">'+
              '<div class="wcLoadingBackground"></div>'+
                '<div class="wcLoadingIconContainer">'+
                  '<i class="wcLoadingIcon fa fa-spinner fa-pulse"></i>'+
                '</div>'+
              '</div>';
        // Define list of nodes on which view data option appears
        var supported_nodes = [
           'table', 'view', 'mview',
           'foreign_table', 'catalog_object', 'partition'
        ],

        /* Enable/disable View data menu in tools based
         * on node selected. if selected node is present
         * in supported_nodes, menu will be enabled
         * otherwise disabled.
         */
        view_menu_enabled = function(obj) {
          if(!_.isUndefined(obj) && !_.isNull(obj))
            return (_.indexOf(supported_nodes, obj._type) !== -1 ? true: false);
          else
            return false;
        },

        /* Enable/disable Query tool menu in tools based
         * on node selected. if selected node is present
         * in unsupported_nodes, menu will be disabled
         * otherwise enabled.
         */
        query_tool_menu_enabled = function(obj) {
          if(!_.isUndefined(obj) && !_.isNull(obj)) {
            if(_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
              if (obj._type == 'database' && obj.allowConn)
                return true;
              else if(obj._type != 'database')
                return true;
              else
                return false;
             } else {
                return false;
             }
          } else {
            return false;
          }
        };

        // Define the nodes on which the menus to be appear
        var menus = [{
          name: 'query_tool', module: this, applies: ['tools'],
          callback: 'show_query_tool', enable: query_tool_menu_enabled,
          priority: 1, label: gettext('Query Tool'),
          icon: 'fa fa-bolt'
        }];

        // Create context menu
        for (var idx = 0; idx < supported_nodes.length; idx++) {
          menus.push({
            name: 'view_all_rows_context_' + supported_nodes[idx],
            node: supported_nodes[idx], module: this, data: {mnuid: 3},
            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
            category: 'view_data', priority: 101, label: gettext('All Rows')
          },{
            name: 'view_first_100_rows_context_' + supported_nodes[idx],
            node: supported_nodes[idx], module: this, data: {mnuid: 1},
            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
            category: 'view_data', priority: 102, label: gettext('First 100 Rows')
          },{
            name: 'view_last_100_rows_context_' + supported_nodes[idx],
            node: supported_nodes[idx], module: this, data: {mnuid: 2},
            applies: ['context', 'object'], callback: 'show_data_grid', enable: view_menu_enabled,
            category: 'view_data', priority: 103, label: gettext('Last 100 Rows')
          },{
            name: 'view_filtered_rows_context_' + supported_nodes[idx],
            node: supported_nodes[idx], module: this, data: {mnuid: 4},
            applies: ['context', 'object'], callback: 'show_filtered_row', enable: view_menu_enabled,
            category: 'view_data', priority: 104, label: gettext('Filtered Rows...')
         });
        }

        pgAdmin.Browser.add_menu_category('view_data', gettext('View/Edit Data'), 100, '');
        pgAdmin.Browser.add_menus(menus);

        // Creating a new pgAdmin.Browser frame to show the data.
        var dataGridFrameType = new pgAdmin.Browser.Frame({
          name: 'frm_datagrid',
          showTitle: true,
          isCloseable: true,
          isPrivate: true,
          url: 'about:blank'
        });

        // Load the newly created frame
        dataGridFrameType.load(pgBrowser.docker);
      },

      // This is a callback function to show data when user click on menu item.
      show_data_grid: function(data, i) {
        var self = this,
            d = pgAdmin.Browser.tree.itemData(i);
        if (d === undefined) {
          alertify.alert(
            gettext('Data Grid Error'),
            gettext('No object selected.')
          );
          return;
        }

        // Get the parent data from the tree node hierarchy.
        var node = pgBrowser.Nodes[d._type],
          parentData = node.getTreeNodeHierarchy(i);

        // If server, database or schema is undefined then return from the function.
        if (parentData.server === undefined || parentData.database === undefined) {
          return;
        }
        // If schema, view, catalog object all are undefined then return from the function.
        if (parentData.schema === undefined && parentData.view === undefined &&
             parentData.catalog === undefined) {
          return;
        }

        var nsp_name = '';

        if (parentData.schema != undefined) {
            nsp_name = parentData.schema.label;
        }
        else if (parentData.view != undefined) {
            nsp_name = parentData.view.label;
        }
        else if (parentData.catalog != undefined) {
            nsp_name = parentData.catalog.label;
        }
        var url_params = {
          'cmd_type': data.mnuid,
          'obj_type': d._type,
          'sid': parentData.server._id,
          'did': parentData.database._id,
          'obj_id': d._id
        };

        var baseUrl = url_for('datagrid.initialize_datagrid', url_params);
        var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - '
                        + nsp_name + '.' + d.label;

        // Initialize the data grid.
        self.initialize_data_grid(baseUrl, grid_title, '', parentData.server.server_type);
      },

      // This is a callback function to show filtered data when user click on menu item.
      show_filtered_row: function(data, i) {
        var self = this,
            d = pgAdmin.Browser.tree.itemData(i);
        if (d === undefined) {
          alertify.alert(
            gettext('Data Grid Error'),
            gettext('No object selected.')
          );
          return;
        }

        // Get the parent data from the tree node hierarchy.
        var node = pgBrowser.Nodes[d._type],
          parentData = node.getTreeNodeHierarchy(i);

        // If server or database is undefined then return from the function.
        if (parentData.server === undefined || parentData.database === undefined) {
          return;
        }

        // If schema, view, catalog object all are undefined then return from the function.
        if (parentData.schema === undefined && parentData.view === undefined &&
             parentData.catalog === undefined) {
          return;
        }

        var nsp_name = '';

        if (parentData.schema != undefined) {
            nsp_name = parentData.schema.label;
        }
        else if (parentData.view != undefined) {
            nsp_name = parentData.view.label;
        }
        else if (parentData.catalog != undefined) {
            nsp_name = parentData.catalog.label;
        }

        var url_params = {
          'cmd_type': data.mnuid,
          'obj_type': d._type,
          'sid': parentData.server._id,
          'did': parentData.database._id,
          'obj_id': d._id

        };

        var baseUrl = url_for('datagrid.initialize_datagrid', url_params);

        // Create url to validate the SQL filter
        var validateUrl = url_for('datagrid.filter_validate', {
                        'sid': url_params['sid'],
                        'did': url_params['did'],
                        'obj_id': url_params['obj_id'],
                      });
        var grid_title = parentData.server.label + '-' + parentData.database.label + '-'
                        + nsp_name + '.' + d.label;

        // Create filter dialog using alertify
        if (!alertify.filterDialog) {
          alertify.dialog('filterDialog', function factory() {
            return {
              main: function(title, message, baseUrl, validateUrl) {
                this.set('title', title);
                this.message = message;
                this.baseUrl = baseUrl;
                this.validateUrl = validateUrl;
              },

              setup:function() {
                return {
                  buttons:[
                    { text: "OK", className: "btn btn-primary" },
                    { text: "Cancel", className: "btn btn-danger" }
                  ],
                  options: { modal: 0, resizable: true, maximizable: false, pinnable: false}
                };
              },
              build: function() {
                alertify.pgDialogBuild.apply(this)
              },
              prepare:function() {
                var self = this,
                  $content = $(this.message),
                  $sql_filter = $content.find('#sql_filter');

                $(this.elements.body.childNodes[0]).addClass(
                  'dataview_filter_dialog'
                );

                this.setContent($content.get(0));

                // Apply CodeMirror to filter text area.
                this.filter_obj = CodeMirror.fromTextArea($sql_filter.get(0), {
                  lineNumbers: true,
                  mode: "text/x-pgsql",
                  extraKeys: pgBrowser.editor_shortcut_keys,
                  indentWithTabs: pgAdmin.Browser.editor_options.indent_with_tabs,
                  indentUnit: pgAdmin.Browser.editor_options.tabSize,
                  tabSize: pgBrowser.editor_options.tabSize,
                  lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
                  autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
                  matchBrackets: pgAdmin.Browser.editor_options.brace_matching
                });

                setTimeout(function() {
                  // Set focus on editor
                  self.filter_obj.focus();
                }, 500);
              },

              callback: function(closeEvent) {

                if (closeEvent.button.text == gettext("OK")) {
                  var sql = this.filter_obj.getValue();
                  var that = this;

                  // Make ajax call to include the filter by selection
                  $.ajax({
                    url: that.validateUrl,
                    method: 'POST',
                    async: false,
                    contentType: "application/json",
                    data: JSON.stringify(sql),
                    success: function(res) {
                      if (res.data.status) {
                        // Initialize the data grid.
                        self.initialize_data_grid(that.baseUrl, grid_title, sql, parentData.server.server_type);
                      }
                      else {
                        alertify.alert(
                          gettext('Validation Error'),
                            res.data.result
                        );
                      }
                    },
                    error: function(e) {
                      alertify.alert(
                        gettext('Validation Error'),
                        e
                      );
                    }
                  });
                }
              }
            };
          });
        }

        var content = '';
        $.get(url_for('datagrid.filter'),
          function(data) {
            alertify.filterDialog('Data Filter', data, baseUrl, validateUrl)
                    .resizeTo(300, 200);
          }
        );
      },

      get_panel_title: function() {
        // Get the parent data from the tree node hierarchy.
        var tree = pgAdmin.Browser.tree,
            selected_item = tree.selected(),
            item_data = tree.itemData(selected_item);
        var self = this;

        var node = pgBrowser.Nodes[item_data._type],
          parentData = node.getTreeNodeHierarchy(selected_item);

        // If server, database is undefined then return from the function.
        if (parentData.server === undefined) {
          return;
        }
        // If Database is not available then use default db
        var db_label = parentData.database ? parentData.database.label
                                           : parentData.server.db;

        var grid_title = db_label + ' on ' + parentData.server.user.name + '@' +
                parentData.server.label;
        return grid_title;
      },

      initialize_data_grid: function(baseUrl, grid_title, sql_filter, server_type) {
        var self = this;
          self.grid_title = grid_title;

        /* Ajax call to initialize the edit grid, which creates
         * an asynchronous connection and create appropriate query
         * for the selected node.
         */
        $.ajax({
          url: baseUrl,
          method: 'POST',
          dataType: 'json',
          contentType: "application/json",
          data: JSON.stringify(sql_filter),
          success: function(res) {

            /* On successfully initialization find the dashboard panel,
             * create new panel and add it to the dashboard panel.
             */
            var url_params = {
              'trans_id': res.data.gridTransId,
              'is_query_tool': 'false',
              'editor_title': encodeURIComponent(self.grid_title)
            };

            var baseUrl = url_for('datagrid.panel', url_params) +
              "?query_url=&server_type=" + encodeURIComponent(server_type);
            var grid_title = gettext('Edit Data - ') + self.grid_title;
            if (res.data.newBrowserTab) {
              var newWin = window.open(baseUrl, '_blank');

              // add a load listener to the window so that the title gets changed on page load
              newWin.addEventListener("load", function() {
                newWin.document.title = grid_title;
              });
            } else {
              var propertiesPanel = pgBrowser.docker.findPanels('properties');
              var dataGridPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);

              // Set panel title and icon
              dataGridPanel.title('<span title="'+grid_title+'">'+grid_title+'</span>');
              dataGridPanel.icon('fa fa-bolt');
              dataGridPanel.focus();

              // Listen on the panel closed event.
              dataGridPanel.on(wcDocker.EVENT.CLOSED, function() {
                $.ajax({
                  url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}),
                  method: 'GET'
                });
              });

              var openDataGridURL = function(j) {
                // add spinner element
                $(j).data('embeddedFrame').$container.append(self.spinner_el);
                setTimeout(function() {
                  var frameInitialized = $(j).data('frameInitialized');
                  if (frameInitialized) {
                    var frame = $(j).data('embeddedFrame');
                    if (frame) {
                      frame.openURL(baseUrl);
                      frame.$container.find('.wcLoadingContainer').hide(1);
                    }
                  } else {
                    openDataGridURL(j);
                  }
                }, 100);
              };

              openDataGridURL(dataGridPanel);
            }
          },
          error: function(e) {
            alertify.alert(
              gettext('Error'),
              gettext('Query Tool Initialization Error')
            );
          }
        });
      },

      // This is a callback function to show query tool when user click on menu item.
      show_query_tool: function(url, i, panel_title) {
        var self = this,
          sURL = url || '',
          panel_title = panel_title || '',
          d = pgAdmin.Browser.tree.itemData(i);
        if (d === undefined) {
          alertify.alert(
            gettext('Query Tool Error'),
            gettext('No object selected.')
          );
          return;
        }

        // Get the parent data from the tree node hierarchy.
        var node = pgBrowser.Nodes[d._type],
          parentData = node.getTreeNodeHierarchy(i);

        // If server, database is undefined then return from the function.
        if (parentData.server === undefined) {
          return;
        }

        var url_params = {
          'sid': parentData.server._id
        };
        var url_endpoint = 'datagrid.initialize_query_tool'
        // If database not present then use Maintenance database
        // We will handle this at server side
        if (parentData.database) {
          url_params['did'] = parentData.database._id;
          url_endpoint = 'datagrid.initialize_query_tool_with_did';
        }
        var baseUrl = url_for(url_endpoint, url_params);

        $.ajax({
          url: baseUrl,
          method: 'POST',
          dataType: 'json',
          contentType: "application/json",
          success: function(res) {
            var grid_title = self.get_panel_title();
            // Open the panel if frame is initialized
            var url_params = {
              'trans_id': res.data.gridTransId,
              'is_query_tool': 'true',
              'editor_title': encodeURIComponent(grid_title)
            }

            var baseUrl = url_for('datagrid.panel', url_params) +
                '?' + "query_url=" + encodeURI(sURL) + "&server_type=" + encodeURIComponent(parentData.server.server_type);

            // Create title for CREATE/DELETE scripts
            if (panel_title) {
              panel_title =
                sqlEditorUtils.capitalizeFirstLetter(panel_title) + ' script';
            }
            else {
              panel_title = gettext('Query - ') + grid_title;
            }

            if (res.data.newBrowserTab) {
              var newWin = window.open(baseUrl, '_blank');

              // add a load listener to the window so that the title gets changed on page load
              newWin.addEventListener("load", function() {
                newWin.document.title = panel_title;
              });
            } else {
              /* On successfully initialization find the dashboard panel,
               * create new panel and add it to the dashboard panel.
               */
              var propertiesPanel = pgBrowser.docker.findPanels('properties');
              var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);

              // Set panel title and icon
              queryToolPanel.title('<span title="'+panel_title+'">'+panel_title+'</span>');
              queryToolPanel.icon('fa fa-bolt');
              queryToolPanel.focus();

              // Listen on the panel closed event.
              queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
                $.ajax({
                  url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}),
                  method: 'GET'
                });
              });

              var openQueryToolURL = function(j) {
                // add spinner element
                $(j).data('embeddedFrame').$container.append(pgAdmin.DataGrid.spinner_el);
                setTimeout(function() {
                  var frameInitialized = $(j).data('frameInitialized');
                  if (frameInitialized) {
                    var frame = $(j).data('embeddedFrame');
                    if (frame) {
                      frame.openURL(baseUrl);
                      frame.$container.find('.wcLoadingContainer').delay(1000).hide(1);
                    }
                  } else {
                    openQueryToolURL(j);
                  }
                }, 100);
              };

              openQueryToolURL(queryToolPanel);
            }
          },
          error: function(e) {
            alertify.alert(
              gettext("Query Tool Initialize Error")
            );
          }
        });
      }
    };

    return pgAdmin.DataGrid;
  });
