electron = require 'electron'
fs   = require 'fs'
path = require 'path'
url  = require 'url'

# Mapping between hostname and file path.
hostPathMap = {}
hostPathMapNextKey = 0

getHostForPath = (path) ->
  key = "extension-#{++hostPathMapNextKey}"
  hostPathMap[key] = path
  key

getPathForHost = (host) ->
  hostPathMap[host]

# Cache extensionInfo.
extensionInfoMap = {}

getExtensionInfoFromPath = (srcDirectory) ->
  manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
  unless extensionInfoMap[manifest.name]?
    # We can not use 'file://' directly because all resources in the extension
    # will be treated as relative to the root in Chrome.
    page = url.format
      protocol: 'chrome-extension'
      slashes: true
      hostname: getHostForPath srcDirectory
      pathname: manifest.devtools_page
    extensionInfoMap[manifest.name] =
      startPage: page
      name: manifest.name
      srcDirectory: srcDirectory
      exposeExperimentalAPIs: true
    extensionInfoMap[manifest.name]

# The loaded extensions cache and its persistent path.
loadedExtensions = null
loadedExtensionsPath = null

# Persistent loaded extensions.
{app} = electron
app.on 'will-quit', ->
  try
    loadedExtensions = Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key].srcDirectory
    try
      fs.mkdirSync path.dirname(loadedExtensionsPath)
    catch e
    fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
  catch e

# We can not use protocol or BrowserWindow until app is ready.
app.once 'ready', ->
  {protocol, BrowserWindow} = electron

  # Load persistented extensions.
  loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'

  try
    loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
    loadedExtensions = [] unless Array.isArray loadedExtensions
    # Preheat the extensionInfo cache.
    getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
  catch e

  # The chrome-extension: can map a extension URL request to real file path.
  chromeExtensionHandler = (request, callback) ->
    parsed = url.parse request.url
    return callback() unless parsed.hostname and parsed.path?
    return callback() unless /extension-\d+/.test parsed.hostname

    directory = getPathForHost parsed.hostname
    return callback() unless directory?
    callback path.join(directory, parsed.path)
  protocol.registerFileProtocol 'chrome-extension', chromeExtensionHandler, (error) ->
    console.error 'Unable to register chrome-extension protocol' if error

  BrowserWindow::_loadDevToolsExtensions = (extensionInfoArray) ->
    @devToolsWebContents?.executeJavaScript "DevToolsAPI.addExtensions(#{JSON.stringify(extensionInfoArray)});"

  BrowserWindow.addDevToolsExtension = (srcDirectory) ->
    extensionInfo = getExtensionInfoFromPath srcDirectory
    if extensionInfo
      window._loadDevToolsExtensions [extensionInfo] for window in BrowserWindow.getAllWindows()
      extensionInfo.name

  BrowserWindow.removeDevToolsExtension = (name) ->
    delete extensionInfoMap[name]

  # Load persistented extensions when devtools is opened.
  init = BrowserWindow::_init
  BrowserWindow::_init = ->
    init.call this
    @on 'devtools-opened', ->
      @_loadDevToolsExtensions Object.keys(extensionInfoMap).map (key) -> extensionInfoMap[key]