/*jshint browser:true, nonew:false*/
/*global WebInspector, Runtime, InspectorFrontendHost, InspectorBackend, InspectorBackendClass*/
WebInspector.NodeInspectorOverrides = function() {
  this._overridenStrings = {
    'Developer Tools - %s': 'Node Inspector - %s',
    '(no domain)': '(core modules)'
  };
  this._overrideProtocolJsonPath();
  this._overrideMainScriptType();
  this._overrideUIStrings();
  this._overrideWebSocketCreate();

  this._setWorkerTitle();

  this._mergeConnectionQueryParams();

  this._openMainScriptOnStartup();

  this._enableDebuggerUINotifications();
};

WebInspector.NodeInspectorOverrides.prototype = {
  _overrideProtocolJsonPath: function() {
    InspectorBackend.orig_loadFromJSONIfNeeded = InspectorBackend.loadFromJSONIfNeeded;
    InspectorBackend.loadFromJSONIfNeeded = function(url) {
      if (url == '../protocol.json')
        return this.orig_loadFromJSONIfNeeded('protocol.json');
    };
  },
  _overrideMainScriptType: function() {
    WebInspector.ResourceTreeModel.prototype.orig_createResourceFromFramePayload =
      WebInspector.ResourceTreeModel.prototype._createResourceFromFramePayload;

    WebInspector.ResourceTreeModel.prototype._createResourceFromFramePayload =
      function(frame, url, type, mimeType) {
        // Force Script type for all node frames.
        // Front-end assigns Document type (i.e. HTML) to our main script file.
        if (frame._isNodeInspectorScript) {
          type = WebInspector.resourceTypes.Script;
        }

        return this.orig_createResourceFromFramePayload(frame, url, type, mimeType);
      };
  },

  _overrideUIStrings: function() {
    var overridenStrings = this._overridenStrings;
    WebInspector.orig_UIString = WebInspector.UIString;
    WebInspector.UIString = function(string, vararg) {
      var args = Array.prototype.slice.call(arguments);
      args[0] = overridenStrings[string] || string;
      return this.orig_UIString.apply(this, args);
    };
  },

  _overrideWebSocketCreate: function() {
    InspectorBackendClass.WebSocketConnection.origCreate =
      InspectorBackendClass.WebSocketConnection.Create;

    InspectorBackendClass.WebSocketConnection.Create = function(url, onConnectionReady) {
      var args = Array.prototype.slice.call(arguments);
      // If front-end is loaded via HTTPS, WebSocket protocol needs to be
      // changed to WSS. Otherwise the browser will block the connection.
      var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
      args[0] = url.replace(/^ws:/, protocol);
      this.origCreate.apply(this, args);
    };
  },

  _setWorkerTitle: function() {
    // Front-end uses `eval location.href` to get url of inspected page
    // This does not work in node.js from obvious reasons, and cause
    // a 'null' message to be printed in front-end console.
    // Since Preferences.applicationTitle does not include inspected url,
    // we can return arbitrary string as inspected URL.
    WebInspector.WorkerTargetManager.prototype._calculateWorkerInspectorTitle = function() {
      InspectorFrontendHost.inspectedURLChanged('');
    };
  },

  _openMainScriptOnStartup: function() {
    WebInspector.targetManager.addModelListener(
      WebInspector.ResourceTreeModel,
      WebInspector.ResourceTreeModel.EventTypes.CachedResourcesLoaded,
      showMainAppFile,
      null
    );
  },

  _mergeConnectionQueryParams: function() {
    var params = Runtime._queryParamsObject;
    params['port'] = params['port'] || '5858';
    params['ws'] = params['ws'] || (location.host + location.pathname);
    params['ws'] += /\?/.test(params['ws']) ? '&' : '?';
    params['ws'] += 'port=' + params['port'];
  },

  _notifications: [],

  _enableDebuggerUINotifications: function() {
    if (!window.Notification) return;

    function closeAllNotifications() {
      this._notifications.forEach(closeNotification);
      this._notifications = [];
    }

    function clickOnNotification(event) {
      closeNotification(this);
      window.focus();
    }

    function closeNotification(notification) {
      notification.removeEventListener('click', clickOnNotification);
      notification.close();
    }

    Notification.requestPermission(function (permission) {
      if (permission !== 'granted') return;

      WebInspector.targetManager.addModelListener(
        WebInspector.DebuggerModel,
        WebInspector.DebuggerModel.Events.DebuggerPaused,
        processDebuggerPaused,
        this
      );

      window.addEventListener('click', closeAllNotifications.bind(this));
      window.addEventListener('focus', closeAllNotifications.bind(this));
    }.bind(this));

    function setMessageTitle(message, reason, currentFrame) {
      switch (reason) {
        case 'exception':
          message.title = 'Exception caught';
          break;
        default:
          message.title = 'Debugger paused';
          break;
      }
    }

    function setMessageHeader(message, reason, currentFrame) {
      if (!currentFrame) return;

      switch (reason) {
        case 'exception':
          message.header = 'Exception caught';
          break;
        default:
          message.header = 'Breakpoint caught';
          break;
      }
    }

    function setMessageFrameInfo(message, reason, currentFrame) {
      if (!currentFrame) return;

      var lineNumber = currentFrame._location.lineNumber;
      var columnNumber = currentFrame._location.columnNumber;
      var scriptId = currentFrame._location.scriptId;
      var sourceURL = currentFrame._script.sourceURL;
      var sourceMapForId = currentFrame._target._sourceMapForScriptId;

      if (sourceMapForId[scriptId] != null) {
        var sourceMapEntry = sourceMapForId[scriptId].findEntry(lineNumber, columnNumber);
        sourceURL = sourceMapEntry[2];
        lineNumber = sourceMapEntry[3];
        columnNumber = sourceMapEntry[4];
      }

      if (sourceURL) {
        sourceURL = sourceURL.substr(sourceURL.lastIndexOf('/')+1);
      }

      message.frameInfo = ' in module ' +
        (sourceURL || '(' + scriptId + ')') +
        ' at line ' + (lineNumber + 1) +
        ' column ' + columnNumber;
    }

    function processDebuggerPaused(event) {
      if (document.hasFocus()) return;

      var reason = event.data.reason;
      var currentFrame = event.data.callFrames[0];

      var message = {
        title: '',
        header: '',
        frameInfo: ''
      };

      setMessageTitle(message, reason, currentFrame);
      setMessageHeader(message, reason, currentFrame);
      setMessageFrameInfo(message, reason, currentFrame);

      var notification = new Notification(message.title, {
        tag: location.href,
        body: [
          message.header,
          message.frameInfo
        ].join(''),
        icon: 'favicon.ico'
      });

      notification.addEventListener('click', clickOnNotification);

      this._notifications.push(notification);
    }
  }
};

new WebInspector.NodeInspectorOverrides();


function showMainAppFile() {
  WebInspector.inspectorView.showPanel('sources').then(function(panel) {
    var fileTabs = panel._sourcesView._editorContainer._files;

    if (Object.keys(fileTabs).length > 0){
      // Some files are already opened - do not change user's workspace
      return;
    }

    var uiSourceCodes = getAllUiSourceCodes();
    var uriToShow = WebInspector.inspectedPageURL;

    for (var i in uiSourceCodes) {
      if (uiSourceCodes[i].url !== uriToShow) continue;
      panel.showUISourceCode(uiSourceCodes[i]);
      return true;
    }

    console.error('Cannot show the main application file ', uriToShow);
  });
}

function getAllUiSourceCodes() {
  // Based on FilteredItemSectionDialog.js > SelectUISourceCodeDialog()
  var projects = WebInspector.workspace.projects();
  var uiSourceCodes = [];
  var projectFiles;

  for (var i = 0; i < projects.length; ++i) {
    projectFiles = projects[i]
      .uiSourceCodes()
      .filter(nameIsNotEmpty);
    uiSourceCodes = uiSourceCodes.concat(projectFiles);
  }

  return uiSourceCodes;

  function nameIsNotEmpty(p) {
    return p.name();
  }
}
