'use strict';

const EventEmitter = require('events').EventEmitter;
const deprecate = require('electron').deprecate;
const ipcMain = require('electron').ipcMain;
const NavigationController = require('electron').NavigationController;
const Menu = require('electron').Menu;

const binding = process.atomBinding('web_contents');
const debuggerBinding = process.atomBinding('debugger');

let  slice = [].slice;
let nextId = 0;

let getNextId = function() {
  return ++nextId;
};

let PDFPageSize = {
  A5: {
    custom_display_name: "A5",
    height_microns: 210000,
    name: "ISO_A5",
    width_microns: 148000
  },
  A4: {
    custom_display_name: "A4",
    height_microns: 297000,
    name: "ISO_A4",
    is_default: "true",
    width_microns: 210000
  },
  A3: {
    custom_display_name: "A3",
    height_microns: 420000,
    name: "ISO_A3",
    width_microns: 297000
  },
  Legal: {
    custom_display_name: "Legal",
    height_microns: 355600,
    name: "NA_LEGAL",
    width_microns: 215900
  },
  Letter: {
    custom_display_name: "Letter",
    height_microns: 279400,
    name: "NA_LETTER",
    width_microns: 215900
  },
  Tabloid: {
    height_microns: 431800,
    name: "NA_LEDGER",
    width_microns: 279400,
    custom_display_name: "Tabloid"
  }
};

// Following methods are mapped to webFrame.
const webFrameMethods = [
  'executeJavaScript',
  'insertText',
  'setZoomFactor',
  'setZoomLevel',
  'setZoomLevelLimits'
];

let wrapWebContents = function(webContents) {
  // webContents is an EventEmitter.
  var controller, method, name, ref1;
  webContents.__proto__ = EventEmitter.prototype;

  // Every remote callback from renderer process would add a listenter to the
  // render-view-deleted event, so ignore the listenters warning.
  webContents.setMaxListeners(0);

  // WebContents::send(channel, args..)
  webContents.send = function() {
    var args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    var channel = arguments[0];
    if (channel == null) {
      throw new Error('Missing required channel argument');
    }
    return this._send(channel, slice.call(args));
  };

  // The navigation controller.
  controller = new NavigationController(webContents);
  ref1 = NavigationController.prototype;
  for (name in ref1) {
    method = ref1[name];
    if (method instanceof Function) {
      (function(name, method) {
        return webContents[name] = function() {
          return method.apply(controller, arguments);
        };
      })(name, method);
    }
  }

  // Mapping webFrame methods.
  for (let method of webFrameMethods) {
    webContents[method] = function() {
      let args = Array.prototype.slice.call(arguments);
      this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args);
    };
  }

  // Make sure webContents.executeJavaScript would run the code only when the
  // webContents has been loaded.
  const executeJavaScript = webContents.executeJavaScript;
  webContents.executeJavaScript = function(code, hasUserGesture) {
    if (this.getURL() && !this.isLoading())
      return executeJavaScript.call(this, code, hasUserGesture);
    else
      return this.once('did-finish-load', executeJavaScript.bind(this, code, hasUserGesture));
  };

  // Dispatch IPC messages to the ipc module.
  webContents.on('ipc-message', function(event, packed) {
    var args, channel;
    channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
    return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
  });
  webContents.on('ipc-message-sync', function(event, packed) {
    var args, channel;
    channel = packed[0], args = 2 <= packed.length ? slice.call(packed, 1) : [];
    Object.defineProperty(event, 'returnValue', {
      set: function(value) {
        return event.sendReply(JSON.stringify(value));
      }
    });
    return ipcMain.emit.apply(ipcMain, [channel, event].concat(slice.call(args)));
  });

  // Handle context menu action request from pepper plugin.
  webContents.on('pepper-context-menu', function(event, params) {
    var menu;
    menu = Menu.buildFromTemplate(params.menu);
    return menu.popup(params.x, params.y);
  });

  // This error occurs when host could not be found.
  webContents.on('did-fail-provisional-load', function() {
    var args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];

    // Calling loadURL during this event might cause crash, so delay the event
    // until next tick.
    return setImmediate((function(_this) {
      return function() {
        return _this.emit.apply(_this, ['did-fail-load'].concat(slice.call(args)));
      };
    })(this));
  });

  // Delays the page-title-updated event to next tick.
  webContents.on('-page-title-updated', function() {
    var args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    return setImmediate((function(_this) {
      return function() {
        return _this.emit.apply(_this, ['page-title-updated'].concat(slice.call(args)));
      };
    })(this));
  });

  // Deprecated.
  deprecate.rename(webContents, 'loadUrl', 'loadURL');
  deprecate.rename(webContents, 'getUrl', 'getURL');
  deprecate.event(webContents, 'page-title-set', 'page-title-updated', function() {
    var args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    return this.emit.apply(this, ['page-title-set'].concat(slice.call(args)));
  });
  return webContents.printToPDF = function(options, callback) {
    var printingSetting;
    printingSetting = {
      pageRage: [],
      mediaSize: {},
      landscape: false,
      color: 2,
      headerFooterEnabled: false,
      marginsType: 0,
      isFirstRequest: false,
      requestID: getNextId(),
      previewModifiable: true,
      printToPDF: true,
      printWithCloudPrint: false,
      printWithPrivet: false,
      printWithExtension: false,
      deviceName: "Save as PDF",
      generateDraftData: true,
      fitToPageEnabled: false,
      duplex: 0,
      copies: 1,
      collate: true,
      shouldPrintBackgrounds: false,
      shouldPrintSelectionOnly: false
    };
    if (options.landscape) {
      printingSetting.landscape = options.landscape;
    }
    if (options.marginsType) {
      printingSetting.marginsType = options.marginsType;
    }
    if (options.printSelectionOnly) {
      printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly;
    }
    if (options.printBackground) {
      printingSetting.shouldPrintBackgrounds = options.printBackground;
    }
    if (options.pageSize && PDFPageSize[options.pageSize]) {
      printingSetting.mediaSize = PDFPageSize[options.pageSize];
    } else {
      printingSetting.mediaSize = PDFPageSize['A4'];
    }
    return this._printToPDF(printingSetting, callback);
  };
};

// Wrapper for native class.
let wrapDebugger = function(webContentsDebugger) {
  // debugger is an EventEmitter.
  webContentsDebugger.__proto__ = EventEmitter.prototype;
};

binding._setWrapWebContents(wrapWebContents);
debuggerBinding._setWrapDebugger(wrapDebugger);

module.exports.create = function(options) {
  if (options == null) {
    options = {};
  }
  return binding.create(options);
};