$(function() {
  $(document).ajaxSend(function(event, request, settings) {
    // Add authenticity token to POST requests
    if (settings.type.toUpperCase() == 'POST') {
      settings.data = (settings.data ? settings.data + "&" : "") + "authenticity_token=" + encodeURIComponent(AUTH_TOKEN);
    }
  });

  // jQuery Tools Configuration
  $.extend($.tools.overlay.conf, {
    expose: {
      color: '#000',
      opacity: 0.9
    }
  });

  Loco2.loading = function() {
    return $('<img src="/images/loading.gif" class="loading_gif">')
  };

  Loco2.apply_loading_mask = function(el) {
    var mask = $('<div />')
    mask.addClass('ajax_loading_mask').css({width: el.width(), height: el.height()})
    el.append(mask)
  };

  Loco2.load = function(what, where, callback) {
    Loco2.apply_loading_mask(where);
    where.load(what, function() {
      if (typeof callback != "undefined") { callback() }
    })
  };

  Loco2.refresh = function() {
    window.onbeforeunload = null;
    window.location.href = window.location.href + '?' + Date.parse(new Date());
  };

  /* A Form in an Overlay */
  Loco2.ModalForm = $.klass({
    initialize: function(options) {
      this.loading = Loco2.loading()
    },

    before_submit: function(data, form, opts) {
      this.element.find(".buttons ol").append($('<li class="loading"></li>').append(this.loading))
    },

    success: function(response) {
      // requests submitted via iframe are always considered successful
      // that's why we also need to check response body
      var r = $.trim(response);
      switch(r) {
        case '':
          Loco2.overlay.close();
          Loco2.overlay.getTrigger().trigger('success.overlay');
          break;
        case 'refresh':
          Loco2.refresh();
          break;
        default:
          this.element.parents('.wrapper').html(response);
      }
    },

    error: function(xhr) {
      if (xhr.status == 422) {
        this.element.parents('.wrapper').html(xhr.responseText);
      }
    },

    complete: function(xhr, text_status) {
      this.loading.remove();
    },

    onsubmit: function(event) {
      event.preventDefault()
      this.element.find('input[type="submit"]').attr('disabled', true)
      this.element.ajaxSubmit({
        beforeSubmit: $.bind(this.before_submit, this),
        success:      $.bind(this.success, this),
        error:        $.bind(this.error, this),
        complete:     $.bind(this.complete, this),
        // in case the request is sent via iframe we need this hack to let
        // the server know that it's an AJAX request
        data:         {'xhr': true}
      })
    }
  })
  $('#overlay form[target!="_blank"]').attach(Loco2.ModalForm)

  Loco2.modal = false;

  /* AJAX Overlay */
  $('a[rel="#overlay"]').livequery(function() {
    $(this).overlay({
      onBeforeLoad: function() {
        Loco2.overlay = this;
        Loco2.modal = true;
        var wrap = this.getContent().find(".wrapper");
        wrap.html('').load(this.getTrigger().attr("href"));
      },
      onBeforeClose: function() {
        Loco2.modal = false;
      },
      closeOnClick: false
    })
  })

  /* Close Overlay Link */
  $('a.close_overlay').live('click', function(event) {
    Loco2.overlay.close();
    event.preventDefault();
  });

  /* Tabs */
  $(".tabs ul").livequery(function() {
    $(this).tabs('.tabs div.tab')
  });

  /* Tooltips */
  $('.has_tooltip').livequery(function() {
    $(this).tooltip({
      position: 'bottom center', 
      relative: true, 
      offset: [-5, 0],
      onShow: function() { this.getTip().bgiframe() }
    })
  });

  // Sliding panel with a toggle
  Loco2.SlidingContentToggle = $.klass({
    initialize: function() {
      this.content = this.element.siblings('.sliding_content');
    },

    onclick: function(event) {
      event.preventDefault();
      this.content.slideToggle();
      this.element.toggleClass('slid_down');
    }
  });
  $('.sliding_content_toggle').attach(Loco2.SlidingContentToggle);

  // Fire cuffon every time a page is loaded in the overlay
  $('#overlay h2.title').livequery(function() {
    Cufon.replace('#overlay h2.title');
  });
});


// Everything relative to GMaps is here

// Parses coords of the form: coords:32.52,52.15
Loco2.parse_coords = function(element) {
  var classes = $(element).attr("class").split(" ");
  var coords = $.grep(classes, function(cls) {
    return cls.match(/^coords\:/);
  })[0];
  coords = coords.replace(/^coords\:/, "").split(",");
  return new GLatLng(parseFloat(coords[0]), parseFloat(coords[1]));
};

// Parses paths of the form path:[42.52,12.45],[23.52,43.24]
Loco2.parse_path = function(element) {
  var classes = $(element).attr("class").split(" ");
  var path = jQuery.grep(classes, function(cls) {
    return cls.match(/^path\:/);
  })[0];
  path = path.replace(/^path\:\[/, "").replace(/\]$/, "").split("],[");
  path = $.map(path, function(coords) {
    coords = coords.split(",");
    return new GLatLng(parseFloat(coords[0]), parseFloat(coords[1]));
  });
  return path;
};

// Load the map (if there is one)
Loco2.load_map = function() {
  if (GBrowserIsCompatible() && $("#map").length > 0) {
    Loco2.map = new GMap2($("#map")[0]);

    if (!Loco2.disable_map_ui) Loco2.map.addControl(new GSmallMapControl());;
    Loco2.map.addMapType(G_NORMAL_MAP);
    // G_PHYSICAL_MAP is the terrain map
    Loco2.map.addMapType(G_PHYSICAL_MAP);
    Loco2.map.setMapType(G_PHYSICAL_MAP);
  };
};

if (typeof GMap2 != "undefined") {
  // Declare loco specific map icon, copying most attributes from default GICon object
  Loco2.map_icon = new GIcon(G_DEFAULT_ICON);
  Loco2.map_icon.image = "/images/icons/gmap_placemarker.png";
  Loco2.map_icon.iconSize = new GSize(8, 8);
  Loco2.map_icon.shadow = "/images/icons/gmap_placemarker.png";
  Loco2.map_icon.shadowSize = new GSize(8, 8);
  Loco2.map_icon.iconAnchor = new GPoint(4, 4);
  Loco2.map_icon.imageMap = [1, 1, 6, 1, 6, 6, 6, 1];

  // We don't want to go in to the street level if we're at the start of a journey!
  Loco2.max_zoom_level = 6;
  // Used to display locations
  Loco2.location_zoom_level = 14;

  $(Loco2.load_map);

  window.onunload = GUnload;
};

Loco2.show_map = function() {
  if (Loco2.map.isLoaded()) {
    Loco2.map.setZoom(Loco2.journey_map_zoom);
    Loco2.map.panTo(Loco2.journey_map_center);
  } else {
    Loco2.map.setCenter(Loco2.journey_map_center, Loco2.journey_map_zoom);
  };
};

Loco2.set_center_and_zoom_for_bounds = function(bounds) {
  Loco2.journey_map_center = bounds.getCenter()
  Loco2.journey_map_zoom = Math.min(Loco2.map.getBoundsZoomLevel(bounds), Loco2.max_zoom_level)
}

// Sometimes the path and the locations are different, for example for an incomplete journey
Loco2.draw_journey = function(path) {
  var polyline = new GPolyline(path, "#0000ff", 2);
  if (typeof Loco2.journey_map_center == 'undefined') {
    Loco2.set_center_and_zoom_for_bounds(polyline.getBounds())
  }

  Loco2.show_map()

  Loco2.map.clearOverlays();
  $.each(path, function() {
    Loco2.map.addOverlay(new GMarker(this, {icon: Loco2.map_icon}));
  });
  Loco2.map.addOverlay(polyline);
};

Loco2.draw_journey_steps = function() {
  var path = new Array();
  var overlays = new Array();

  $.each(Loco2.journey_steps, function(step_id) {
    var step = this;

    if (step.type == 'end_point' || step.type == 'stop') {
      var point = new GLatLng(step.coordinates[0], step.coordinates[1]);
      path.push(point);
      var overlay = new GMarker(point, {icon: G_DEFAULT_ICON});
    } else {
      var start = new GLatLng(step.coordinates[0][0], step.coordinates[0][1]);
      var finish = new GLatLng(step.coordinates[1][0], step.coordinates[1][1]);
      path.push(start);
      path.push(finish);
      var overlay = new GPolyline([start, finish], "#0000ff", 2);
    }

    GEvent.addListener(overlay, 'click', function() {
      $('#step_' + step_id).click();
    })

    overlays.push(overlay);
  })

  var bounds = (new GPolyline(path)).getBounds()
  Loco2.set_center_and_zoom_for_bounds(bounds)

  Loco2.show_map()

  Loco2.map.setMapType(G_PHYSICAL_MAP);
  Loco2.map.clearOverlays();
  $(overlays).each(function() { Loco2.map.addOverlay(this) })
}

Loco2.reset_map = function() {
  Loco2.map.setMapType(G_PHYSICAL_MAP)
  Loco2.map.setZoom(Loco2.journey_map_zoom)
  Loco2.map.panTo(Loco2.journey_map_center)
}

Loco2.zoom_to_step = function(step_id) {
  var step = Loco2.journey_steps[step_id]
  if (step.type == 'end_point' || step.type == 'stop') {
    var center = new GLatLng(step.coordinates[0], step.coordinates[1]);
    var zoom = Loco2.location_zoom_level;
    var type = G_NORMAL_MAP;
  } else {
    var start = new GLatLng(step.coordinates[0][0], step.coordinates[0][1])
    var finish = new GLatLng(step.coordinates[1][0], step.coordinates[1][1])
    var bounds = (new GPolyline([start, finish])).getBounds()
    var center = bounds.getCenter()
    var zoom = Loco2.map.getBoundsZoomLevel(bounds)
    var type = G_PHYSICAL_MAP;
  }
  Loco2.map.setMapType(type);
  Loco2.map.setZoom(zoom)
  Loco2.map.panTo(center)
};


Loco2.GeocodingField = $.klass({
  initialize: function() {
    this.id_element = $('#' + this.element.attr('id') + '_id');
    this.value = this.element.val();
    this.message = $('<span />');
    this.element.after(this.message);
    this.lookup_button = $('<input type="button" value="Look-up" class="button" />');
    this.element.after(this.lookup_button);
    this.lookup_button.hide();
    this.loading = Loco2.loading();
  },

  set_value: function(data) {
    this.value = data.name;
    this.element.val(this.value);
    this.id_element.val(data.id);
    this.id_element.trigger('loco2.change');
  },

  changed: function() {
    return this.value != this.element.val();
  },

  onblur: function() {
    this.lookup();
  },

  onkeyup: function() {
    this.lookup_button.toggle(this.changed());
  },

  lookup: function() {
    var self = this
    var value = $.trim(this.element.val())
    if (value == '' || this.value == value) { return }

    $.ajax({
      url:        this.url,
      type:       'POST',
      beforeSend: $.bind(this.before_send, this),
      data:       { 'name': value },
      dataType:   'json',
      success:    $.bind(this.successful_lookup, this),
      error:      $.bind(this.error, this),
      complete:   function() { self.loading.remove() }
    })
  },

  successful_lookup: function(data) {
    this.set_value(data);
    this.message.html("If you didn't mean <strong>" + data.name + ", " + data.country + "</strong>, please try typing it in differently");
  },

  error: function(request, status, error) {
    this.message.html(request.responseText)
    this.lookup_button.show()
  },

  before_send: function() {
    this.lookup_button.hide()
    this.element.after(this.loading)
  }
})

Loco2.LocationField = $.klass(Loco2.GeocodingField, {
  initialize: function($super) {
    $super()
    this.url = Loco2.url_root + '/locations'
    var station_field_id = '#' + this.element.attr('id').replace('location', 'station')
    if (this.station_field = $(station_field_id).attached(Loco2.StationField)[0]) {
      this.station_field.reset()
    }
  },

  successful_lookup: function($super, data) {
    $super(data)
    if (this.station_field) {
      this.station_field.load_stations(data.id)
    }
  },

  before_send: function($super) {
    if (this.station_field) {
      this.station_field.reset()
    }
    $super()
  }
})

Loco2.StationField = $.klass(Loco2.GeocodingField, {
  initialize: function($super) {
    $super()
    this.url = Loco2.url_root + '/stations'
    this.element.autocomplete([], {
      formatItem: function(item) { return item.name },
      matchContains: true,
      max: 100,
      minChars: 0,
      selectFirst: false
    })
    this.set_suggestions(window[this.element.attr('id') + '_suggestions'] || [])
  },

  onresult: function(event, selected) {
    if (selected) {
      this.set_value(selected)
      this.message.html('')
    } else {
      this.lookup()
    }
  },

  onblur: function() {
    // when an autocompletion suggestion is selected
    // the field looses focus earlier than the click occurs
    // that's why we need to postpone this handler
    var self = this;
    setTimeout(function() { self.element.search() }, 100)
  },

  onkeyup: function() {
    this.lookup_button.toggle(this.changed() && !this.element.hasClass('completing'))
  },

  enable: function() { this.element.attr('disabled', false).focus() },
  disable: function() { this.element.attr('disabled', true) },

  reset: function() {
    this.disable()
    this.element.val('')
    this.id_element.val('')
    this.message.html('')
    this.element.setOptions({data: []})
  },

  load_stations: function(location_id) {
    var self = this
    $.ajax({
      url:        Loco2.url_root + '/locations/' + location_id + '/stations',
      beforeSend: function() { self.element.after(self.loading) },
      dataType:   'json',
      success:    function(data) {
        self.set_suggestions(data)
        self.enable()
      },
      complete:   function() { self.loading.remove() }
    })
  },

  set_suggestions: function(suggestions) {
    this.element.setOptions({data: suggestions})
    if (suggestions.length > 0) {
      this.message.html('Start typing or press the down arrow on your keyboard to see a list of suggestions')
    } else {
      this.set_value({name: '', id: null});
      this.message.html("There is no stations in the database. Leave blank to just use the location.");
    }
  }
})

Loco2.NewJourney = $.klass({
  initialize: function() {
    this.fields = this.element.find('li.string')
    this.fields.filter(':first').show()
    this.button = this.element.find('input[type=submit]')
    this.button.attr('disabled', true)
    // Disable browser's autocompletion for text fields
    this.fields.find('input[type=text]').attr('autocomplete', 'off')
    this.completed = false
    // Monitor hidden fields
    this.element.find('input[type=hidden]').bind('loco2.change', $.bind(this.change, this))
  },

  onsubmit: function(event) {
    if (!this.completed) {
      event.preventDefault()
      return false
    }
  },

  change: function(event) {
    var field = $(event.target).parent('li')
    field.prev().addClass('completed')
    field.next().show().find('input').focus()
    this.check_completeness()
  },

  check_completeness: function() {
    if (this.fields.filter('.completed').length == this.fields.length) {
      this.completed = true
      this.button.attr('disabled', false)
    }
  }
})

$(function() {
  $('input.station').attach(Loco2.StationField)
  $('input.location').attach(Loco2.LocationField)
  $('form#new_journey').attach(Loco2.NewJourney)
})

// Temporary thing for the geonames search form
Loco2.GeonamesSearch = $.klass({
  initialize: function() {
    var container = this.element.parent()
    this.query_field = container.find('input[name="query"]')
    this.results_number = container.find('input[name="results_number"]')
    this.feature_codes = container.find('input[name="feature_codes"]')
    this.results_container = this.element.parent().find('div.results')
    this.loading = Loco2.loading()
  },

  onclick: function() {
    var feature_codes = $.map(this.feature_codes.filter(':checked').val().split(' '), function(c) { return "featureCode=" + c }).join('&')
    var url = 'http://ws.geonames.org/searchJSON?' + feature_codes  + '&callback=?'
    var data = {
      'q': this.query_field.val(),
      'maxRows': this.results_number.val(),
      'continentCode': 'EU',
      'style': 'FULL'
    }
    this.element.after(this.loading)
    $.getJSON(url, data, $.bind(this.display_results, this))
  },

  display_results: function(data) {
    this.loading.remove()
    var results = this.results_container
    results.html('')
    results.append('<p>Total Results:' + data.totalResultsCount + '</p>')
    $.each(data.geonames, function() {
      var alt_names = this.alternateNames ? $.map(this.alternateNames, function(n) { return n.name }).join(', ') : ''
      results.append('<p><strong>' + this.name + ', ' + this.countryName + '</strong> ' + this.fcodeName + ' (' + alt_names + ')</p>')
    })
  }
})

$(function() {
  $('input#geonames_search').attach(Loco2.GeonamesSearch)
})
