// ==UserScript==
// @name     Websocket debugger
// @namespace thomas-rosenau.de
// @version  1
// @grant    none
// @include     *
// @run-at   document-start
// ==/UserScript==

((main) => {
  if (window === window.top) {
    // http://stackoverflow.com/a/5006952/27862
    let script = document.createElement('script');
    script.textContent = `try{(${main})();}catch(e){console.log(e);}`;
    if (!document.head) { return; }
    document.head.appendChild(script).parentNode.removeChild(script);
  }
})(() => {
  
  let log = (color, i, key, ...args) => {
    switch(key) {
    case 'onmessage':
      console.log(`%c[WS${i}]%c↓%c${args[0].data}`, `float: left; color: ${color};`, 'font-size: 120%; float: left', 'word-break: break-all; font-size: 110%; float: left; margin: 0 .5em; font-family: Arial, sans-serif; padding: .5em; background: #f4f8f6; border-radius: .5em; box-shadow: 0 1px 1px gray');
      break;
    case 'send':
      console.log(`%c[WS${i}]%c↑%c${args[0]}`, `float: left; color: ${color};`, 'font-size: 120%; float: left',  'word-break: break-all; font-size: 110%; float: right; margin: 0 .5em 0 4em; font-family: Arial, sans-serif; padding: .5em; background: rgba(224, 251, 199); border-radius: .5em; box-shadow: 0 1px 1px gray');
      break;
    case 'onclose':
      console.log(`%c[WS${i}]%c close ${args[0] ? args[0].target.url : ''}`, `float: left; color: ${color};`);
      break;
    default:
      console.log(`%c[WS${i}]%c`, `color: ${color};`, '', key.replace(/^on/, ''), ...args);
    }
  };

  const events = ['onclose', 'onerror', 'onmessage', 'onopen'];

  let i = 0;

  let construct = (WebSocket, args) => {

    let clog = log.bind(null, `hsl(${300 + 135 * i}, 70%, 50%)`);
    i ++;

    clog(i, 'new', ...args);

    let handler = ((i) => ({
      get(connection, prop) {
        if (typeof connection[prop] === 'function') {
          return (...args) => {
            clog(i, prop, ...args);
            return connection[prop](...args);
          };
        }
        return connection[prop];
      },
      set(connection, prop, handler) {
        let value = handler;
        if (events.includes(prop)) {
          value = (...args) => {
            clog(i, prop, ...args);
            return handler(...args);
          };
        }
        connection[prop] = value;
        return true;
      },
      deleteProperty(connection, prop) {
        if (events.includes(prop)) {
          connection[prop] = clog.bind(null, i, prop);
        } else {
          delete connection[prop];
        }
      }
    }))(i);

    let result = new Proxy(new WebSocket(...args), handler);
    events.forEach(evt => delete result[evt]); // init logging
    return result;
  };

  window.WebSocket = new Proxy(WebSocket, {construct});

});