send = (message) ->
  (window.opener or window.parent).postMessage(JSON.stringify(message), '*')

id = window.location.hash.replace('#', '')
initOptions = {}
_authToken = null

messenger = {
  load: -> send({t: 'w', id: id})
  notify: (ev, data) ->
    ev = ev.type || ev
    send(t: 'ee', id: id, d: data, e: ev)
}

loadScript = (url, callback) ->
    head = document.getElementsByTagName('head')[0]
    script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = url

    script.onreadystatechange = callback
    script.onload = callback

    head.appendChild(script)

# A function to be called when the composition iframe is ready
ready = (_, data = {}) ->
  # get the composition iframe by id
  preview = data.preview or data.source.frames[id]
  return unless preview
  preview.Blogvio.auth.token _authToken
  window.require._require.push preview.require

  # initialize the Blogvio.api relay
  {composition, widget, user} = preview.getData()

  initEditor composition, widget, preview, user

initEditor = (composition, widget, remote, user) ->
  ($body = $ 'body').toggleClass 'is-popup', !!window.opener
  Editor = require("../index")
  premium = user?.premium || false

  Container = require('../../core/services/container').default
  Container.init({
    user,
    uploadToken: user.upload_token
  })

  window.editor = new Editor {
    el: $body,
    remote, initOptions, premium
  }

  if composition
    editor.edit(composition.id)
      .then(notifyInit)
      .fail((e) -> console.log(e.stack))
  else
    editor.new(widget.id)
      .then(notifyInit)
      .fail((e) -> console.log(e.stack))

  editor.bind('skin:select', (skin) -> messenger.notify('skin:select', skin))
  editor.$bind('skin:save', (_, skin) -> messenger.notify('skin:save', skin))
  editor.$bind('skin:destroy', (_, skin) -> messenger.notify('skin:destroy', skin))
  editor.$bind('composition:save', messenger.notify)
  editor.$bind('composition:temporary:save', messenger.notify)
  editor.$bind('content:auth-req', messenger.notify)
  editor.$bind('step:intent', messenger.notify)
  editor.$bind('Control:upgrade', messenger.notify)
  editor.$bind('Content:upgrade', messenger.notify)

  $(window).on 'unload', window.editor.release.bind editor

  return editor

notifyInit = ->
  messenger.notify('editor:init')

# relay mouse and keyboard events to the parent frame
eventToObject = (event) ->
  res = {}
  for key, value of event
    continue if typeof value is 'object'
    res[key] = value
  return res

relayEvent = (ev) ->
  Widgetic.Event.on window, ev, (ev) ->
    messenger.notify Widgetic.UI.Editor.RELAY, eventToObject(ev)

# Adds postMessage event listeners and notifies that the editor
# iframe is ready
run = ->
  receivers = {
    'ready': ready
    'step': (e) -> window.editor.step(e.d)
    'token': (e) ->
      if e.d
        Blogvio.auth.token(_authToken = e.d)
        window.editor?.preview.remote.Blogvio.auth.token _authToken
      else Blogvio.auth.disconnect()
    'cont': (e) ->
      window.editor.el.trigger "content:#{e.d.source}:add", items: e.d.data
    'opts': (e) ->
      return initOptions = e.d unless window.editor
      if window.editor.composition then window.editor.setOptions e.d else window.editor.setInitOptions e.d
    'ree': (e) -> relayEvent(e.d)
  }

  wl = window.location
  lo = decodeURIComponent (wl.search.match(/(?:lo=)([^\#\&$]*)/) or [0,wl.origin])[1]
  receiver = (event) ->
    return unless event.origin is lo
    try
      data = JSON.parse(event.data)
      receivers[data.t]?(data, source: event.source)
    catch e
      debugger if window.localStorage?.debug_on_error
      console.error e.stack
    return

  Blogvio.Event.on window, 'load', ->
    # bind listener for `message`
    Blogvio.Event.on(window, 'message', receiver)

    # notify load
    messenger.load()

  # on window unload release the editor and restore the preview
  Blogvio.Event.on window, 'unload', ->
    window.editor.release()

module.exports = {run, ready}
