/* global $, ActionCable */

import { indexOf } from 'lodash'

import Active from './Active'
import Control from './Control'
import DataModel from './DataModel'
import Map from './Map'
import Mapper from './Mapper'
import Synapse from './Synapse'
import Topic from './Topic'
import { ChatView } from './Views'
import Visualize from './Visualize'

const Cable = {
  init: () => {
    let self = Cable
    self.cable = ActionCable.createConsumer()
  },
  subscribeToMap: id => {
    let self = Cable
    self.sub = self.cable.subscriptions.create({
      channel: 'MapChannel',
      id: id
    }, {
      received: event => self[event.type](event)
    })
  },
  unsubscribeFromMap: () => {
    let self = Cable
    self.sub.unsubscribe()
    delete self.sub
  },
  synapseAdded: event => {
    // we receive contentless models from the server
    // containing only the information we need to determine whether the active mapper
    // can view this synapse and the two topics it connects,
    // then if we determine it can, we make a call for the full model
    const m = Active.Mapper
    const s = new DataModel.Synapse(event.synapse)
    const t1 = new DataModel.Topic(event.topic1)
    const t2 = new DataModel.Topic(event.topic2)

    if (t1.authorizeToShow(m) && t2.authorizeToShow(m) && s.authorizeToShow(m) && !DataModel.Synapses.get(event.synapse.id)) {
      // refactor the heck outta this, its adding wicked wait time
      var topic1, topic2, node1, node2, synapse, mapping, cancel, mapper

      const waitThenRenderSynapse = () => {
        if (synapse && mapping && mapper) {
          topic1 = synapse.getTopic1()
          node1 = topic1.get('node')
          topic2 = synapse.getTopic2()
          node2 = topic2.get('node')

          Synapse.renderSynapse(mapping, synapse, node1, node2, false)
        } else if (!cancel) {
          setTimeout(waitThenRenderSynapse, 10)
        }
      }

      mapper = DataModel.Mappers.get(event.synapse.user_id)
      if (mapper === undefined) {
        Mapper.get(event.synapse.user_id, function(m) {
          DataModel.Mappers.add(m)
          mapper = m
        })
      }
      $.ajax({
        url: '/synapses/' + event.synapse.id + '.json',
        success: function(response) {
          DataModel.Synapses.add(response)
          synapse = DataModel.Synapses.get(response.id)
        },
        error: function() {
          cancel = true
        }
      })
      $.ajax({
        url: '/mappings/' + event.mapping_id + '.json',
        success: function(response) {
          DataModel.Mappings.add(response)
          mapping = DataModel.Mappings.get(response.id)
        },
        error: function() {
          cancel = true
        }
      })
      waitThenRenderSynapse()
    }
  },
  synapseUpdated: event => {
    // TODO: handle case where permission changed
    var synapse = DataModel.Synapses.get(event.id)
    if (synapse) {
      // edge reset necessary because fetch causes model reset
      var edge = synapse.get('edge')
      synapse.fetch({
        success: function(model) {
          model.set({ edge: edge })
          model.trigger('changeByOther')
        }
      })
    }
  },
  synapseRemoved: event => {
    var synapse = DataModel.Synapses.get(event.id)
    if (synapse) {
      var edge = synapse.get('edge')
      var mapping = synapse.getMapping()
      if (edge.getData('mappings').length - 1 === 0) {
        Control.hideEdge(edge)
      }

      var index = indexOf(edge.getData('synapses'), synapse)
      edge.getData('mappings').splice(index, 1)
      edge.getData('synapses').splice(index, 1)
      if (edge.getData('displayIndex')) {
        delete edge.data.$displayIndex
      }
      DataModel.Synapses.remove(synapse)
      DataModel.Mappings.remove(mapping)
    }
  },
  topicAdded: event => {
    const m = Active.Mapper
    // we receive a contentless model from the server
    // containing only the information we need to determine whether the active mapper
    // can view this topic, then if we determine it can, we make a call for the full model
    const t = new DataModel.Topic(event.topic)

    if (t.authorizeToShow(m) && !DataModel.Topics.get(event.topic.id)) {
      // refactor the heck outta this, its adding wicked wait time
      var topic, mapping, mapper, cancel

      const waitThenRenderTopic = () => {
        if (topic && mapping && mapper) {
          Topic.renderTopic(mapping, topic, false, false)
        } else if (!cancel) {
          setTimeout(waitThenRenderTopic, 10)
        }
      }

      mapper = DataModel.Mappers.get(event.topic.user_id)
      if (mapper === undefined) {
        Mapper.get(event.topic.user_id, function(m) {
          DataModel.Mappers.add(m)
          mapper = m
        })
      }
      $.ajax({
        url: '/topics/' + event.topic.id + '.json',
        success: function(response) {
          DataModel.Topics.add(response)
          topic = DataModel.Topics.get(response.id)
        },
        error: function() {
          cancel = true
        }
      })
      $.ajax({
        url: '/mappings/' + event.mapping_id + '.json',
        success: function(response) {
          DataModel.Mappings.add(response)
          mapping = DataModel.Mappings.get(response.id)
        },
        error: function() {
          cancel = true
        }
      })
      waitThenRenderTopic()
    }
  },
  topicUpdated: event => {
    // TODO: handle case where permission changed
    var topic = DataModel.Topics.get(event.id)
    if (topic) {
      var node = topic.get('node')
      topic.fetch({
        success: function(model) {
          model.set({ node: node })
          model.trigger('changeByOther')
        }
      })
    }
  },
  topicMoved: event => {
    var topic, node, mapping
    if (Active.Map) {
      topic = DataModel.Topics.get(event.id)
      mapping = DataModel.Mappings.get(event.mapping_id)
      mapping.set('xloc', event.x)
      mapping.set('yloc', event.y)
      if (topic) node = topic.get('node')
      if (node) node.pos.setc(event.x, event.y)
      Visualize.mGraph.plot()
    }
  },
  topicRemoved: event => {
    var topic = DataModel.Topics.get(event.id)
    if (topic) {
      var node = topic.get('node')
      var mapping = topic.getMapping()
      Control.hideNode(node.id)
      DataModel.Topics.remove(topic)
      DataModel.Mappings.remove(mapping)
    }
  },
  messageCreated: event => {
    if (Active.Mapper && Active.Mapper.id === event.message.user_id) return
    ChatView.addMessages(new DataModel.MessageCollection(event.message))
  },
  mapUpdated: event => {
    var map = Active.Map
    var couldEditBefore = map.authorizeToEdit(Active.Mapper)
    var idBefore = map.id
    map.fetch({
      success: function(model, response) {
        var idNow = model.id
        var canEditNow = model.authorizeToEdit(Active.Mapper)
        if (idNow !== idBefore) {
          Map.leavePrivateMap() // this means the map has been changed to private
        } else if (couldEditBefore && !canEditNow) {
          Map.cantEditNow()
        } else if (!couldEditBefore && canEditNow) {
          Map.canEditNow()
        } else {
          model.trigger('changeByOther')
        }
      }
    })
  }
}

export default Cable
