/*!
   --------------------------------
   Waterfall.js
   --------------------------------
   + https://github.com/raphamorim/waterfall
   + version 1.1.0
   + Copyright 2016 Raphael Amorim & Israel Teixeira
   + Licensed under the MIT license
   + Documentation: https://github.com/raphamorim/waterfall
*/

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define('waterfall', function () {
      return factory
    })
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory
  } else {
    root.waterfall = factory
  }
}(this, function waterfall (container) {
  if (typeof (container) === 'string') {
    container = document.querySelector(container)
  }

  function style (el) {
    return window.getComputedStyle(el)
  }

  function margin (name, el) {
    return parseFloat(style(el)['margin' + name]) || 0
  }

  function px (n) { return parseFloat(n) + 'px' }
  function y (el) { return parseFloat(el.style.top) }
  function x (el) { return parseFloat(el.style.left) }
  function width (el) { return parseFloat(style(el).width) }
  function height (el) { return parseFloat(style(el).height) }
  function bottom (el) { return y(el) + height(el) + margin('Bottom', el) }
  function right (el) { return x(el) + width(el) + margin('Right', el) }

  function sort (l) {
    l = l.sort(function (a, b) {
      var bottomDiff = bottom(b) - bottom(a)
      return bottomDiff || x(b) - x(a)
    })
  }

  function Boundary (firstRow) {
    var els = firstRow
    sort(els)

    this.add = function (el) {
      els.push(el)
      sort(els)
      els.pop()
    }

    this.min = function () { return els[els.length - 1] }
    this.max = function () { return els[0] }
  }

  function placeEl (el, top, left) {
    el.style.position = 'absolute'
    el.style.top = px(top)
    el.style.left = px(left)
  }

  function placeFirstElement (el) {
    placeEl(el, 0, margin('Left', el))
  }

  function placeAtTheFirstLine (prev, el) {
    placeEl(el, prev.style.top, right(prev) + margin('Left', el))
  }

  function placeAtTheSmallestColumn (minEl, el) {
    placeEl(el, bottom(minEl) + margin('Top', el), x(minEl))
  }

  function adjustContainer (container, maxEl) {
    container.style.position = 'relative'
    container.style.height = px(bottom(maxEl) + margin('Bottom', maxEl))
  }

  function thereIsSpace (els, i) {
    return right(els[i - 1]) + width(els[i]) <= width(container)
  }

  var els = container.children

  if (els.length) {
    placeFirstElement(els[0])
  }

  for (var i = 1; i < els.length && thereIsSpace(els, i); i++) {
    placeAtTheFirstLine(els[i - 1], els[i])
  }

  var firstRow = [].slice.call(els, 0, i)
  var boundary = new Boundary(firstRow)

  for (; i < els.length; i++) {
    placeAtTheSmallestColumn(boundary.min(), els[i])
    boundary.add(els[i])
  }

  adjustContainer(container, boundary.max())
}))
