/* global $ */

import _ from 'lodash'

import Active from './Active'
import Control from './Control'
import DataModel from './DataModel'
import GlobalUI, { ReactApp } from './GlobalUI'
import Settings from './Settings'
import Visualize from './Visualize'

const Filter = {
  dataForPresentation: {
    metacodes: {},
    mappers: {},
    synapses: {}
  },
  filters: {
    metacodes: [],
    mappers: [],
    synapses: []
  },
  visible: {
    metacodes: [],
    mappers: [],
    synapses: []
  },
  reset: function() {
    var self = Filter
    self.filters.metacodes = []
    self.filters.mappers = []
    self.filters.synapses = []
    self.visible.metacodes = []
    self.visible.mappers = []
    self.visible.synapses = []
    self.dataForPresentation.metacodes = {}
    self.dataForPresentation.mappers = {}
    self.dataForPresentation.synapses = {}
    ReactApp.render()
  },
  // an abstraction function for checkMetacodes, checkMappers, checkSynapses to reduce
  // code redundancy
  updateFilters: function(collection, propertyToCheck, correlatedModel, filtersToUse, listToModify) {
    var self = Filter
    var newList = []
    var removed = []
    var added = []
    // the first option enables us to accept
    // ['Topics', 'Synapses'] as 'collection'
    if (typeof collection === 'object') {
      DataModel[collection[0]].each(function(model) {
        var prop = model.get(propertyToCheck)
        if (prop !== null) {
          prop = prop.toString()
          if (newList.indexOf(prop) === -1) {
            newList.push(prop)
          }
        }
      })
      DataModel[collection[1]].each(function(model) {
        var prop = model.get(propertyToCheck)
        if (prop !== null) {
          prop = prop.toString()
          if (newList.indexOf(prop) === -1) {
            newList.push(prop)
          }
        }
      })
    } else if (typeof collection === 'string') {
      DataModel[collection].each(function(model) {
        var prop = model.get(propertyToCheck)
        if (prop !== null) {
          prop = prop.toString()
          if (newList.indexOf(prop) === -1) {
            newList.push(prop)
          }
        }
      })
    }
    removed = _.difference(self.filters[filtersToUse], newList)
    added = _.difference(newList, self.filters[filtersToUse])
    _.each(removed, function(identifier) {
      const index = self.visible[filtersToUse].indexOf(identifier)
      self.visible[filtersToUse].splice(index, 1)
      delete self.dataForPresentation[filtersToUse][identifier]
    })
    _.each(added, function(identifier) {
      const model = DataModel[correlatedModel].get(identifier) ||
      DataModel[correlatedModel].find(function(m) {
        return m.get(propertyToCheck) === identifier
      })
      self.dataForPresentation[filtersToUse][identifier] = model.prepareDataForFilter()
      self.visible[filtersToUse].push(identifier)
    })
    // update the list of filters with the new list we just generated
    self.filters[filtersToUse] = newList
    ReactApp.render()
  },
  checkMetacodes: function() {
    var self = Filter
    self.updateFilters('Topics', 'metacode_id', 'Metacodes', 'metacodes', 'metacode')
  },
  checkMappers: function() {
    var self = Filter
    if (Active.Map) {
      self.updateFilters('Mappings', 'user_id', 'Mappers', 'mappers', 'mapper')
    } else {
      // on topic view
      self.updateFilters(['Topics', 'Synapses'], 'user_id', 'Creators', 'mappers', 'mapper')
    }
  },
  checkSynapses: function() {
    var self = Filter
    self.updateFilters('Synapses', 'desc', 'Synapses', 'synapses', 'synapse')
  },
  filterAllMetacodes: function(toVisible) {
    var self = Filter
    self.visible.metacodes = toVisible ? self.filters.metacodes.slice() : []
    ReactApp.render()
    self.passFilters()
  },
  filterAllMappers: function(toVisible) {
    var self = Filter
    self.visible.mappers = toVisible ? self.filters.mappers.slice() : []
    ReactApp.render()
    self.passFilters()
  },
  filterAllSynapses: function(toVisible) {
    var self = Filter
    self.visible.synapses = toVisible ? self.filters.synapses.slice() : []
    ReactApp.render()
    self.passFilters()
  },
  // an abstraction function for toggleMetacode, toggleMapper, toggleSynapse
  // to reduce code redundancy
  // gets called in the context of a list item in a filter box
  toggleLi: function(whichToFilter, id) {
    var self = Filter
    if (self.visible[whichToFilter].indexOf(id) === -1) {
      self.visible[whichToFilter].push(id)
    } else {
      const index = self.visible[whichToFilter].indexOf(id)
      self.visible[whichToFilter].splice(index, 1)
    }
    ReactApp.render()
    self.passFilters()
  },
  toggleMetacode: function(id) {
    var self = Filter
    self.toggleLi('metacodes', id)
  },
  toggleMapper: function(id) {
    var self = Filter
    self.toggleLi('mappers', id)
  },
  toggleSynapse: function(id) {
    var self = Filter
    self.toggleLi('synapses', id)
  },
  passFilters: function() {
    var self = Filter
    var visible = self.visible

    var passesMetacode, passesMapper, passesSynapse

    var opacityForFilter = Active.Map ? 0 : 0.4

    DataModel.Topics.each(function(topic) {
      var n = topic.get('node')
      var metacodeId = topic.get('metacode_id').toString()

      if (visible.metacodes.indexOf(metacodeId) === -1) passesMetacode = false
      else passesMetacode = true

      if (Active.Map) {
        // when on a map,
        // we filter by mapper according to the person who added the
        // topic or synapse to the map
        let userId = topic.getMapping().get('user_id').toString()
        if (visible.mappers.indexOf(userId) === -1) passesMapper = false
        else passesMapper = true
      } else {
        // when on a topic view,
        // we filter by mapper according to the person who created the
        // topic or synapse
        let userId = topic.get('user_id').toString()
        if (visible.mappers.indexOf(userId) === -1) passesMapper = false
        else passesMapper = true
      }

      if (passesMetacode && passesMapper) {
        if (n) {
          n.setData('alpha', 1, 'end')
        } else {
          console.log(topic)
        }
      } else {
        if (n) {
          Control.deselectNode(n, true)
          n.setData('alpha', opacityForFilter, 'end')
          n.eachAdjacency(function(e) {
            Control.deselectEdge(e, true)
          })
        } else {
          console.log(topic)
        }
      }
    })

    // flag all the edges back to 'untouched'
    DataModel.Synapses.each(function(synapse) {
      var e = synapse.get('edge')
      e.setData('touched', false)
    })
    DataModel.Synapses.each(function(synapse) {
      var e = synapse.get('edge')
      var desc
      var userId = synapse.get('user_id').toString()

      if (e && !e.getData('touched')) {
        var synapses = e.getData('synapses')

        // if any of the synapses represent by the edge are still unfiltered
        // leave the edge visible
        passesSynapse = false
        for (let i = 0; i < synapses.length; i++) {
          desc = synapses[i].get('desc')
          if (visible.synapses.indexOf(desc) > -1) passesSynapse = true
        }

        // if the synapse description being displayed is now being
        // filtered, set the displayIndex to the first unfiltered synapse if there is one
        var displayIndex = e.getData('displayIndex') ? e.getData('displayIndex') : 0
        var displayedSynapse = synapses[displayIndex]
        desc = displayedSynapse.get('desc')
        if (passesSynapse && visible.synapses.indexOf(desc) === -1) {
          // iterate and find an unfiltered one
          for (let i = 0; i < synapses.length; i++) {
            desc = synapses[i].get('desc')
            if (visible.synapses.indexOf(desc) > -1) {
              e.setData('displayIndex', i)
              break
            }
          }
        }

        if (Active.Map) {
          // when on a map,
          // we filter by mapper according to the person who added the
          // topic or synapse to the map
          userId = synapse.getMapping().get('user_id').toString()
        }
        if (visible.mappers.indexOf(userId) === -1) passesMapper = false
        else passesMapper = true

        var color = Settings.colors.synapses.normal
        if (passesSynapse && passesMapper) {
          e.setData('alpha', 1, 'end')
          e.setData('color', color, 'end')
        } else {
          Control.deselectEdge(e, true)
          e.setData('alpha', opacityForFilter, 'end')
        }

        e.setData('touched', true)
      } else if (!e) {
        console.log(synapse)
      }
    })

    // run the animation
    Visualize.mGraph.fx.animate({
      modes: ['node-property:alpha',
        'edge-property:alpha'],
      duration: 200
    })
  }
}

export default Filter
