class Autocomplete

constructor: (scope,factory_name, element,$filter)->
  @element     = element
  @attributes  = element[0].attributes
  @scope       = scope
  @model_name  = @attributes['ng-model'].value
  @list_model  = @attributes['ng-list-model'].value.split('.')
  @options     = if @attributes['ng-list-options']  then eval("(" + @attributes['ng-list-options'].value + ")") else {}
  @context     = if @attributes['ng-context']       then @attributes['ng-context'].value
  @sort_by     = if @attributes['ng-sort-by']       then @attributes['ng-sort-by'].value else @list_attr
  @list_attr   = @list_model[1]
  @factory     = factory_name(@list_model[0])
  @scopes      = @context.split('.') if @context
  @parent_name = @scopes.pop() if @scopes
  @list        = angular.element("<div class='autocomplete menu'></div>")
  @filter      = $filter
  @existing_factory = @scope[@factory] || @scope.$parent[@factory]
  @listFactory = @element.injector().get(@factory) unless @existing_factory
  @list.insertAfter(@element[0])

  if @parent_name
    @parent_id   = @parent_name + if @parent_name.indexOf('_id') < 0 then '_id' else ''

  @load()

parent_context: =>
  hash = {}
  hash[@parent_id] = @parent() if @context
  return hash
serialize: =>
  hash = {}
  for key,val of @options
    hash[key] = val
  for key,val of @parent_context()
    hash[key] = val
  hash
model: (val)=>
  return @scope.$eval(@model_name + '="' + val + '"') if val
  return @scope.$eval(@model_name)
parent: =>
  return null unless @context
  return @scope.$eval(@context)
load: ->
  scope   = @scope
  factory = @factory
  model   = @model
  updateView = @updateView
  if @context
    return unless @parent()
  if @existing_factory
    scope[factory] = @existing_factory
    scope.$watchCollection factory, (newVal) ->
      updateView(model())
    updateView(model())
  else
    @listFactory.index @serialize(), (data) ->
      scope[factory] = data
      updateView(model())
updateView: (value) =>
  object              = {}
  object[@list_attr]  = value || ''
  scope               = @scope
  model               = @model
  model_name          = @model_name
  filtered            = @filter('filter')((scope[@factory] || []), object )
  filtered            = @filter('orderBy')(filtered, @sort_by)
  items               = []
  for item in filtered
    item = angular.element "<a class='item'>" + item[@list_attr] + "</a>"
    item.bind 'click', (event) ->
      model(event.target.textContent)
      scope.$eval event.target.parentNode.previousSibling.attributes['ng-change-on-blur'].value
    items.push item
  @list.empty()
  @list.append(items)

angular.module('AutoComplete', [ 'FactoryName'])

.directive 'ngAutocomplete', ->
  restrict: 'E'
  replace:  true
  require:  'ngModel'
  require:  'ngListModel'
  template: (element, attributes) ->
    newElement = angular.element('<input>')
    newElement.addClass('autocomplete')
    newElement[0].setAttribute('ng-update',attributes.ngModel)
    newElement[0].setAttribute('auto-complete',true)
    return newElement[0].outerHTML
  controller: ($scope, $element, $filter, $timeout, factoryName) ->
    ac = new Autocomplete($scope,factoryName,$element,$filter)
    if ac.context
      $scope.$watch ac.context, (newVal, oldVal) ->
        if newVal
          ac.load()
    $scope.$watch ac.model_name, (newVal) ->
      ac.updateView(newVal)

.directive 'autoComplete', ->
  restrict: 'A'
  require: '?ngModel'
  link: (scope, element, attributes, ngModel) ->
    if element[0].tagName == 'INPUT'
      element.bind 'focus', ->
        pos = element.position() if element.position
        element.next()[0].style.left = '0px'
      element.bind 'blur', ->
        active = angular.element(element.next()[0].getElementsByClassName('active')[0])
        active.removeClass('active')
        ngModel.$setViewValue(element.val())
        ngModel.$render()
      element.bind 'keydown', (input)->
        keypress = (direction) ->
          index = if direction == 'next' then 0 else element.next().find('a').length - 1
          selected = angular.element(element.next()[0].getElementsByClassName('active')[0])
          if selected.hasClass('active')
            selected.removeClass('active')
            until complete
              selected = angular.element(selected[0][direction + 'Sibling']) if selected[0]
              complete = !!selected[0]
              complete = selected[0].tagName == 'A' if complete
              complete = true if !selected[0]
          selected = angular.element(element.next().find('a')[index]) unless selected[0]
          ind = 0
          for el,i in element.next()[0].getElementsByTagName('a')
            ind = i if el == selected[0]
          scroll = selected[0].scrollHeight * ind
          selected[0].parentElement.scrollTop = scroll
          selected.addClass('active')
          element.val(selected.text())
        if input.keyCode == 40
          keypress('next')
        if input.keyCode == 38
          keypress('previous')