// borrowed from: http://www.movable-type.co.uk/scripts/latlong.html

// extend Number object with methods for converting degrees/radians
Number.prototype.toRad = function() {  // convert degrees to radians
  return this * Math.PI / 180;
};

var search = (function(){

  var t;
  var categories = $H({
    nearby: '*',
    restaurants: 'restaurants+OR+food+OR+groceries',
    cafes: 'cafes+OR+coffee+OR+bars',
    shopping: 'shopping+OR+stores',
    entertainment: 'entertainment+OR+theatres+OR+nightclubs',
    health: 'health+OR+beauty'
  });
  var selectedCategory = 'nearby';
  
  var url = new Template('http://ajax.googleapis.com/ajax/services/search/local?'
    + 'v=1.0&q=#{query}#{category}&mrt=yp&sll=#{latitude},#{longitude}&rsz=large&callback=processResults');

  var createNewResultElement = function(result){
    var a = new Element('a', {'class': 'result_item', href: '#result'});
    a.insert(new Element('div', { 'class': 'number' }).update((result.n + 1) + '. '));
    a.insert(new Element('div', { 'class': 'name' }).update(result.name));
    a.insert(new Element('div', { 'class': 'address' }).update(' - ' + result.address));
    a.insert(new Element('div', { 'class': 'distance' }).update(' - ' + result.distance));
    a.insert(new Element('img', { src: '/images/small-blue-arrow.png' }));
    return a;
  };

  var infoWindowMarkup = [
    "<span id='geocoder'>",
      "<label for='geocode_address'>If the marker is wrong, enter your address.</label><br />",
      "<input type='text' id='geocode_address' />",
      "<button id='geocoder_button' onclick='window.search.geocodeAddress(); return false;'>Find Address</button>",
      "<br />",
      "<small>(or drag the marker around)</small>",
    "</span>"
  ].join('');

  var map, marker, infoWindow = null, p, result = {};
  
  // start out with coordinates of OpinionLab's Chicago office
  var defaultCoordinates = { latitude: 42.186319, longitude: -87.800052 };
  //var centerOfUSA = { latitude: 37.037778, longitude: -95.626389 };
  
  // store all the different zoom level that are needed
  var zoomLevels = { _default: 4, rough: 13, specific: 16 };

  var coordinates = defaultCoordinates;
  var zoomLevel = zoomLevels._default;

  function tryToUseRoughCoordinates(){
    if (google.loader.ClientLocation == null) return;
    coordinates = google.loader.ClientLocation;
    zoomLevel = zoomLevels.rough;
    if (map) map.setZoom(zoomLevel);
    updateCoordinates(coordinates.latitude, coordinates.longitude);
  }

  function tryToUseExistingCoordinates () {
    if ($F('user_location_lat').empty() || $F('user_location_lat').empty()) {
      return false;
    } else {
      updateCoordinates(Number($F('user_location_lat')), Number($F('user_location_lng')), $F('user_location_range'));
      zoomLevel = zoomLevels.specific;
      if (map) map.setZoom(zoomLevel);
      return true;
    }
  }

  function tryToUseExistingOrRoughCoordinates () {
    // if there are no existing coordinates, try to use the rough ones instead
    if (!tryToUseExistingCoordinates()) tryToUseRoughCoordinates();
  }

  function initializeMap(){
    if (search.map != undefined) return;
    map = new google.maps.Map($('map'), {
      zoom: zoomLevel,
      center: new google.maps.LatLng(coordinates.latitude, coordinates.longitude),
      mapTypeId: google.maps.MapTypeId.ROADMAP,
      mapTypeControl: false
    });
    search.map = map;
    map.setZoom(zoomLevel);
    updateMarkerOnMap();
    tryToGetMoreSpecificCoordinates();
  }

  
  // setup DOM
  document.observe('dom:loaded', function(){

    // monitor search field
    var searchField = $('search_field');
    searchField.observe('keyup', function(e){
      // if the value is different
      if (t) clearTimeout(t);
      t = setTimeout(function(){
        document.fire('input:changed');
      }, 250);
      e.stop();
    });

    searchField.observe('focus', function(e){
      if ($F(searchField) == 'filter your results') {
        clearSelectedLocation();
      }
    });

    searchField.observe('blur', function(e){
      document.fire('input:blurred');
    });
    $('results').on('click', '.result_item', function(e){
      document.fire('result_item:selected', { element: e.findElement('.result_item') });
      e.stop();
    });
    document.observe('input:changed', getNewResults);
    document.observe('input:blurred', getNewResults);
    document.observe('result_item:selected', selectItem);
    
    function showGeoLocationSlide(e){
      $('intro').hide();
      $('leaving_feedback').show();
      $('business_location').hide();
      $('geolocation').show();
      removeActiveClasses();
      addActiveClasses(0);
      initializeMap.defer();
      if (!e) return;
      location.hash = e.findElement('a').readAttribute('href');
      e.findElement().blur();
    }

    function showBusinessLocationSlide(e){
      $('intro').hide();
      $('geolocation').hide();
      $('leaving_feedback').show();
      $('business_location').show();
      removeActiveClasses();
      addActiveClasses(1);
      if (!e) return;
      location.hash = e.findElement('a').readAttribute('href');
      e.findElement().blur();
    }

    function removeActiveClasses(){
      $$('#leaving_feedback .active', '#screenshots .active').invoke('removeClassName', 'active').invoke('removeClassName', 'active');
    }

    function addActiveClasses(i){
      [$$('.feedback_status li')[i], $$('.feedback_status .button_small')[i], $$('#screenshots img')[i]].invoke('addClassName', 'active');
    }

    $('leave_feedback_now').observe('click', showGeoLocationSlide);
    $('view_location_chooser').observe('click', showGeoLocationSlide);
    $('view_business_location_chooser').observe('click', showBusinessLocationSlide);

    // note that the fragment doesn't match an id of an element to prevent the
    // page from scrolling
    if (location.hash.indexOf('#_business_location') != -1){
      showBusinessLocationSlide();
    }
    
    // select the category if there is one stored
    var c = Store.get('category');
    if (categories.get(c)){
      selectCategory(c);
    }

    google.setOnLoadCallback(function(){
      tryToUseExistingOrRoughCoordinates();
      if (location.hash == '#_geolocation') showGeoLocationSlide();
      getNewResults();
    });

    // business categories
    function filterResultsByElement(e){
      var button = e.findElement('.toolbar_button');
      if (button != $(search.selectCategory)) selectCategory(button.id);
      button.blur();
    }

    function selectCategory(name){
      if (!categories.keys().include(name)) return;
      if ($(search.selectedCategory)) $(search.selectedCategory).removeClassName('selected');
      search.selectedCategory = name;
      $(search.selectedCategory).addClassName('selected');
      $('view_business_location_chooser').href = '#_business_location/' + name;
      getNewResults();
    }

    $('business_categories').on('click', '.toolbar_button', filterResultsByElement);
    // end business categories

  });
  // end setup DOM

  
  // try to get higher res coordinates if the browser supports some sort of geolocation
  function tryToGetMoreSpecificCoordinates () {
    var gl = navigator.geolocation || google.gears && google.gears.factory.create('beta.geolocation');
    if (gl) {
      gl.getCurrentPosition(function(position){
        // if it works
        updateCoordinates(position.coords.latitude, position.coords.longitude, position.coords.accuracy);
        getNewResults();
        updateMarkerOnMap();
        zoomLevel = zoomLevels.specific;
        map.setZoom(zoomLevel);
      }, function(){
        // if it doesn't work
        //_log('We are unable to find a more specific location for your current position.');
      },{ enableHighAccuracy: true });
    } else {
      tryToUseExistingOrRoughCoordinates();
      getNewResults();
      updateMarkerOnMap();
    }
  }
  
  function updateMarkerOnMap (displayInfoWindow) {
    p = new google.maps.LatLng(coordinates.latitude, coordinates.longitude);
    if (marker) {
      marker.setPosition(p);
    } else {
      marker = new google.maps.Marker({
        position: p,
        map: map, 
        title: 'We think you are near this spot.',
        draggable: true
      });
      
      google.maps.event.addListener(marker, 'dragstart', function() {
        if (search.infoWindow) search.infoWindow.close();
      });

      google.maps.event.addListener(marker, 'dragend', function() {
        var latLng = marker.getPosition();
        updateCoordinates(latLng.lat(), latLng.lng(), 10);
        map.setCenter(latLng);
        zoomLevel = zoomLevels.specific;
        map.setZoom(zoomLevel);
        getNewResults();
      });

      google.maps.event.addListener(marker, 'click', function() {
        openInfoWindow(marker);
      });

    }
    map.setCenter(p);
    // todo: refactor this
    if (displayInfoWindow !== false) openInfoWindow(marker);
    if (displayInfoWindow == false) closeInfoWindow(marker);
  }

  function openInfoWindow(marker){
    if (infoWindow == null){
      infoWindow = new google.maps.InfoWindow({
        position: marker.getPosition(), 
        maxWidth: 300,
        content: infoWindowMarkup
      });
      search.infoWindow = infoWindow;
    }

    
    infoWindow.open(map, marker);
  }

  function closeInfoWindow(){
    infoWindow.close();
  }
  
  function updateCoordinates (latitude, longitude, accuracy) {
    // update the coordinates object
    coordinates = { latitude: latitude, longitude: longitude };
    search.coordinates = coordinates;
    // update the fields on the page so the site can remember coordinates
    $('user_location_lat').value = latitude;
    $('user_location_lng').value = longitude;
    $('user_location_range').value = accuracy || '';
  }

  function getNewResults () {
    //console.log('getNewResults');
    if (coordinates) {
      removeOldResults();
      var input = $('search_field');
      $(document.body).insert(new Element('script', { src:
        url.evaluate({
          query: (($F(input) == '' || $F(input) == 'filter your results')? '*' : $F(input)),
          category: ((search.selectedCategory == 'nearby')? '' : '+' + categories.get(search.selectedCategory)),
          latitude: coordinates.latitude,
          longitude: coordinates.longitude
        })
      }));
    }
  }
  
  function removeOldResults () {
    $$('script[src*="callback=processResults"]').invoke('remove');
  }
  
  function processResults (r){
    //_log('processResults');
    var results = $A(r.responseData.results);
    //_log(results);
    //_log('break');
    function whatIsClosest(result){
      // sort by distance, capture the distance
      // formula borrowed from: http://www.movable-type.co.uk/scripts/latlong.html
      var lat1 = Number(result.lat), lat2 = coordinates.latitude;
      var lon1 = result.lng, lon2 = coordinates.longitude;
      var R = 6371; // km
      var dLat = (lat2-lat1).toRad();
      var dLon = (lon2-lon1).toRad();
      var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
              Math.cos((lat1).toRad()) * Math.cos((lat2).toRad()) * 
              Math.sin(dLon/2) * Math.sin(dLon/2); 
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
      var d = R * c;
      //                           convert meters to feet
      result.distance = d * 1000 * 3.28083989501312;
      return result.distance;
    }

    // identifies results with no phoneNumbers, addressLines, or streetAddress
    // or results with equal titles and regions, example: Colorado
    function resultsWithNoBusinessInfo(result){
      return (!result.phoneNumbers && !result.addressLines && result.streetAddress == '' ||
        result.title == result.region);
    }

    function insertResultElement(result, i){
      //_log(result);
      $('results').insert(createNewResultElement({
        n: i,
        name: result.titleNoFormatting,
        distance: formatDistance(result.distance),
        address: result.streetAddress
      }));
    }

    function formatDistance (distance) {
      // if distance (in feet) is greater than 1000,
      if (distance > 1000){
        // format in miles, like 0.8 mi
        return Math.round(distance * 0.000189393939393939 * 100)/100 + " mi";
      } else {
        return Math.round(distance) + " ft";
      }
    }

    var sortedResults = results.sortBy(whatIsClosest);
    var filteredResults = sortedResults.reject(resultsWithNoBusinessInfo);

    search.results = filteredResults;

    $('results').update('');
    filteredResults.each(insertResultElement);

    document.fire('choices:updated');
  }
  
  function selectItem (event) {
    if (event.memo.element == undefined) return;
    Store.set('category', search.selectedCategory);

    var result = search.results[$$('.result_item').indexOf(event.memo.element)];

    $('search_field').value = result.titleNoFormatting;
    $('location_name').value = result.titleNoFormatting;
    $('location_url').value = result.url;
    $('location_gcid').value = result.url.match(/cid=([a-f0-9]+)/i)[1];
    $('location_geo_location_lat').value = result.lat;
    $('location_geo_location_lng').value = result.lng;
    $('location_address_street').value = result.streetAddress;
    $('location_address_city').value = result.city;
    $('location_address_country').value = result.country;
    $('location_address_region').value = result.region;
    $('location_gls_phone_numbers').value = (result.phoneNumbers)? $A(result.phoneNumbers).map(function(num) { return num.number; }).join(',') : '';
    $$('.new_location').first().submit();
  }
  
  function clearSelectedLocation () {
    $('search_field').value = '';
    getNewResults();
  }

  function geocodeAddress (e) {
    // try to geocode address
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({ address: $F('geocode_address') }, function(geocoderResponse){
      // update point on map
      if (!geocoderResponse[0]) return;
      var c = geocoderResponse[0].geometry.location;
      updateCoordinates(c.lat(), c.lng());
      getNewResults();
      zoomLevel = zoomLevels.specific;
      map.setZoom(zoomLevel);
      if (coordinates) updateMarkerOnMap(false);
    });
    if (e) e.stop();
  }

  function showAddressInput(){
    $('guess').hide();
    $('geocoder').show();
    $('geocode_address').focus();
  }

  return {
    processResults: processResults,
    coordinates: coordinates,
    t: t,
    geocodeAddress: geocodeAddress,
    showAddressInput: showAddressInput,
    map: map,
    selectedCategory: selectedCategory
  };
  
})();

var processResults = search.processResults;

function _log (latitude, longitude) {
  if (typeof(window.console) != 'undefined' && console.log)
    console.log(latitude, longitude);
}

