"use strict"
define([ "lib/helper", "lib/gui/signalgraph", "lib/gui/signal"],
function (Helper, SignalGraph, Signal) {

  var graphColors = ["#396AB1", "#DA7C30", "#3E9651", "#CC2529", "#535154", "#6B4C9A", "#922428", "#948B3D"]
  //graphColors = ["#7293CB", "#E1974C", "#84BA5B", "#D35E60", "#808585", "#9067A7", "#AB6857", "#CCC210"];

  var inactiveTime = 200

  function SignalEntry(graph, color, stream) {
    var signal = new Signal(color)
    var remove = graph.add(signal)

    var unsubscribe = stream.onValue(update)

    this.destroy = function () {
      unsubscribe()
      remove()
    }

    this.getSignal = function () {
      return signal
    }

    return this

    function update(d) {
      if ("wifi" in d)
        signal.set(d.wifi.inactive > inactiveTime ? null : d.wifi.signal)
    }
  }

  function TableEntry(parent, nodeInfo, color, stream, mgmtBus, signal) {
    var el = document.createElement("tr")
    parent.appendChild(el)

    var tdHostname = document.createElement("td")
    var tdTQ = document.createElement("td")
    var tdSignal = document.createElement("td")
    var tdDistance = document.createElement("td")
    var tdInactive = document.createElement("td")

    el.appendChild(tdHostname)
    el.appendChild(tdTQ)
    el.appendChild(tdSignal)
    el.appendChild(tdDistance)
    el.appendChild(tdInactive)

    var marker = document.createElement("span")
    marker.textContent = "⬤ "
    marker.style.color = color
    tdHostname.appendChild(marker)

    var hostname = document.createElement("span")
    tdHostname.appendChild(hostname)

    var infoSet = false
    var unsubscribe = stream.onValue(update)

    el.onmouseenter = function () {
      el.classList.add("highlight")
      signal.setHighlight(true)
    }

    el.onmouseleave = function () {
      el.classList.remove("highlight")
      signal.setHighlight(false)
    }

    el.destroy = function () {
      unsubscribe()
      parent.removeChild(el)
    }

    return el

    function update(d) {
      if ("wifi" in d) {
        var signal = d.wifi.signal
        var inactive = d.wifi.inactive

        el.classList.toggle("inactive", inactive > inactiveTime)

        tdSignal.textContent = signal
        tdInactive.textContent = Math.round(inactive / 1000) + " s"
      }

      if ("batadv" in d)
        tdTQ.textContent = Math.round(d.batadv.tq / 2.55) + " %"
      else
        tdTQ.textContent = "‒"

      if (infoSet)
        return

      if ("nodeInfo" in d) {
          infoSet = true

          var link = document.createElement("a")
          link.textContent = d.nodeInfo.hostname
          link.href = "#"
          link.nodeInfo = d.nodeInfo
          link.onclick = function () {
            mgmtBus.pushEvent("goto", this.nodeInfo)
            return false
          }

          while (hostname.firstChild)
            hostname.removeChild(hostname.firstChild)

          hostname.appendChild(link)

          try {
            var distance = Helper.haversine(nodeInfo.location.latitude, nodeInfo.location.longitude,
                                            d.nodeInfo.location.latitude, d.nodeInfo.location.longitude)

            tdDistance.textContent = Math.round(distance * 1000) + " m"
          } catch (e) {
            tdDistance.textContent = "‒"
          }
      } else
        hostname.textContent = d.id
    }
  }

  function Interface(parent, nodeInfo, iface, stream, mgmtBus) {
    var colors = graphColors.slice(0)

    var el = document.createElement("div")
    el.ifname = iface
    parent.appendChild(el)

    var h = document.createElement("h3")
    h.textContent = iface
    el.appendChild(h)

    var table = document.createElement("table")
    var tr = document.createElement("tr")
    table.appendChild(tr)
    table.classList.add("datatable")

    var th = document.createElement("th")
    th.textContent = "Knoten"
    tr.appendChild(th)

    th = document.createElement("th")
    th.textContent = "TQ"
    tr.appendChild(th)

    th = document.createElement("th")
    th.textContent = "dBm"
    tr.appendChild(th)

    th = document.createElement("th")
    th.textContent = "Entfernung"
    tr.appendChild(th)

    th = document.createElement("th")
    th.textContent = "Inaktiv"
    tr.appendChild(th)

    el.appendChild(table)

    var wrapper = document.createElement("div")
    wrapper.className = "signalgraph"
    el.appendChild(wrapper)

    var canvas = document.createElement("canvas")
    canvas.className = "signal-history"
    canvas.height = 200
    wrapper.appendChild(canvas)

    var graph = new SignalGraph(canvas, -100, 0, true)

    var stopStream = stream.skipDuplicates(sameKeys).onValue(update)

    var managedNeighbours = {}

    function update(d) {
      var notUpdated = new Set()
      var id

      for (id in managedNeighbours)
        notUpdated.add(id)

      for (id in d) {
        if (!(id in managedNeighbours)) {
          var neighbourStream = stream.map("."  + id).filter( function (d) { return d !== undefined })
          var color = colors.shift()
          var signal = new SignalEntry(graph, color, neighbourStream)
          managedNeighbours[id] = { views: [ signal,
                                             new TableEntry(table, nodeInfo, color, neighbourStream, mgmtBus, signal.getSignal())
                                           ],
                                    color: color
                                  }
        }

        notUpdated.delete(id)
      }

      for (id in notUpdated) {
        managedNeighbours[id].views.forEach( function (d) { d.destroy() })
        colors.push(managedNeighbours[id].color)
        delete managedNeighbours[id]
      }
    }


    el.destroy = function () {
      stopStream()

      for (var id in managedNeighbours)
        managedNeighbours[id].views.forEach( function (d) { d.destroy() })

      el.removeChild(h)
      el.removeChild(wrapper)
      el.removeChild(table)
    }
  }

  function sameKeys(a, b) {
    a = Object.keys(a).sort()
    b = Object.keys(b).sort()

    return !(a < b || a > b)
  }

  function getter(k) {
    return function(obj) {
      return obj[k]
    }
  }

  return function (nodeInfo, stream, mgmtBus) {
    var stopStream, div

    function render(el) {
      div = document.createElement("div")
      el.appendChild(div)

      stopStream = stream.skipDuplicates(sameKeys).onValue(update)

      function update(d) {
        var have = {}
        var remove = []
        if (div.hasChildNodes()) {
          var children = div.childNodes
          for (var i = 0; i < children.length; i++) {
            var a = children[i]
            if (a.ifname in d)
              have[a.ifname] = true
            else {
              a.destroy()
              remove.push(a)
            }
          }
        }

        remove.forEach(function (d) { div.removeChild(d) })

        for (var k in d) {
          if (!(k in have))
            new Interface(div, nodeInfo, k, stream.map(getter(k)), mgmtBus)
        }
      }
    }

    function destroy() {
      stopStream()

      while (div.firstChild) {
        div.firstChild.destroy()
        div.removeChild(div.firstChild)
      }
    }

    return { title: document.createTextNode("Nachbarknoten")
           , render: render
           , destroy: destroy
           }
  }
})