(function($, m) {

  $(document).ready(function() {
    if ( !document.getElementById('map') ) { return; }

    var $map = $('#map').appendTo('div.content');
    $map.html('');

    var cos = m.cos,
        acos = m.acos,
        sin = m.sin,
        pi = m.PI;

    var mapSettings = {
      tipOffsetX: -30,
      tipOffsetY: 0
    };
    var initialPoint = new google.maps.LatLng(37.020098, -95.625);
    var initialSettings = {
      zoom: 4,
      center: initialPoint,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };

    var map = new google.maps.Map(document.getElementById('map'), initialSettings);
    var mapIcon = new google.maps.MarkerImage('/assets/styles/images/icons/map-dot.png',
      new google.maps.Size(18, 18),
      // origin
      new google.maps.Point(0,0),
      // icon's infoWindow anchor
      new google.maps.Point(0, 18)
    );
    var geocoder = new google.maps.Geocoder();
    var infowindow = new google.maps.InfoWindow();

    var markers = [],
        hideClose,
        closeLink = '<a href="#" class="close">Close</a>',
        pointer = '<span class="pointer"></span>',
        $loadingWrap = $('<div class="loading-overlay"><div class="loading"></div><div id="progress">Loading locations...</div></div>').appendTo('#map'),
        $progress = $('#progress'),
        $loading = $loadingWrap.find('.loading');

    var lWidth = $loading.width() || 200;
    zipSearch();
    // initial retrieval of all location info.
    $.getJSON('/includes/locations/', {}, function(json, textStatus) {

      if (json && json.locations && json.locations.length > 0) {
        var locs = json.locations,
            locLength=json.locations.length,
            tens = -10;

        // store all locations in a globally accessible variable
        $.locs = locs;

        // Load all locations ten at a time, taking a 20ms break after each ten.
        (function loadLocations(ten) {
          window.setTimeout(function() {
            var end = m.min(ten+10,locLength);
            for (i = ten; i < end; i++) {

              addLocation(map, locs[i] );
            }

            if (end < locLength) {
              var percent = m.round(100*end/locLength);
              $loading.width(lWidth - (percent*2) );
              $progress.text(percent + '%');
              loadLocations(tens+=10);
            } else {
              $loading.hide();
              $progress.text('100%');
              $loadingWrap.fadeOut('fast');
              $('#zip-search').addClass('vis');
            }

          }, 20);

        })(tens+=10);

      }
    });

    function addLocation(map, location, distance) {
      var point = new google.maps.LatLng(location.lat, location.lng);

      var marker = new google.maps.Marker({
        position: point,
        map: map,
        icon: mapIcon
      });
      markers.push(marker);
      var locationInfo =  getLocationInfo(location, distance);

      if (distance) {
        $.bounds.extend(point);
      }

      google.maps.event.addListener(marker, "click", function(e) {
        openInfoWindow(locationInfo, marker);
      });

    }

    function openInfoWindow(locationInfo, marker) {
      window.clearInterval(hideClose);
      infowindow.close();
      infowindow.setContent(locationInfo);
      infowindow.open(map, marker);
      $('#maptip')
        .parent().css('overflow','visible')
          .parent().css({
            overflow: 'visible'
          });
      hideClose = window.setInterval(function() {
        var closeImg = $map.find('img[src$=iw_close.gif]');
        if (closeImg.length) {
          closeImg.css('display', 'none');
          window.clearInterval(hideClose);
        }
      }, 20);

    }

    function getLocationInfo(loc, dist) {
      dist = dist || '';
      var dealers = [];
      for (var i=0, dl = loc.dealers.length; i < dl; i++) {
        var dealer = loc.dealers[i],
            dealerInfo = '<h4>Details Sales Contact</h4>';

        dealerInfo += '<div class="dealer-name">' + dealer.title + '</div>';
        dealerInfo += dealer.email ? '<div class="dealer-email"><a href="mailto:'  + dealer.email + '">' + dealer.email + '</a></div>' : '';
        if (dealer.numbers.length) {
          dealerInfo += '<div class="numbers">';

          for (var j=0; j < dealer.numbers.length; j++) {
            if (dealer.numbers[j].number) {
              dealerInfo += '<div class="' + dealer.numbers[j].type + '">' + dealer.numbers[j].number + ' (' + dealer.numbers[j].type + ')</div>';
            }
          }
          dealerInfo += '</div>';
        }
        dealers[i] = dealerInfo;
      }
      var info = [
        '<div id="maptip">',
          '<div class="' + loc.colors.join(' ') + '">',
            '<div class="map-title">',
              '<h3>' + loc.company + '</h3>',
              '<div class="adr">',
                loc.street ? '<div class="street-address">' + loc.street + '</div>' : '',
                '<div>',
                  loc.city ? '<span class="locality">' + loc.city + '</span>, ' : '',
                  '<span class="region">' + loc.state + '</span><span class="postal-code"> ' + loc.zip + '</span>',
                  loc.country && loc.country !== 'US' ? '<div class="country-name">' + loc.country + '</div>' : '',
                '</div>',
              '</div>',
              '<div class="phone">' + loc.phone + '</div>',
            '</div>',
            '<div class="map-body">',
              '<div class="dealers">',
                dealers.join(''),
              '</div>',
              dist,
            '</div>',
            closeLink,
            pointer,
          '</div>',
        '</div>'
      ];

      return info.join('');
    }

    $(document).bind('closemaptip', function(event) {
      infowindow.close();
    });

    $map.delegate('a.close', 'click', function(event) {
      $(document).trigger('closemaptip');
      return false;
    });


    function zipSearch() {

      var formInputs = [
      '<div id="zip-results" class="right"></div>',
      '<div>',
        '<label for="zip">ZIP Code: </label>',
        '<input id="zip" name="zip" type="text" />',
        '<select id="radius" name="radius">',
          '<option value="50" selected>within...</option>',
          '<option value="10">10 miles</option>',
          '<option value="50">50 miles</option>',
          '<option value="100">100 miles</option>',
          '<option value="500">500 miles</option>',
          '<option value="1000">1000 miles</option>',
          '<option value="2000">2000 miles</option>',
        '</select>',
        '<button type="submit" class="btn"><span>Search</span></button>',
      '</div>'
      ];

      var fetchLocations = function(e) {
        e.preventDefault();
        var address = $.trim( $('#zip').val() ),
            isValid = true,
            radius = parseFloat($('#radius').val()) || 50,
            locsLength = $.locs.length;

        $('#zip-results').empty();

        if ( !(/^[a-zA-Z0-9]{3}[- ]*[a-zA-Z0-9]{3}$/).test(address) ) {
          address = address.slice(0,5);
          isValid = (/\d{5}/).test(address);
        }


        if (!address) {
          clearLocations();
          for (var i=0; i < locsLength; i++ ) {
            addLocation(map, $.locs[i]);
          }
          map.setCenter(initialSettings.center);
          map.setZoom(initialSettings.zoom);
          return;
        } else if ( !isValid) {
          $('#zip-results').html('<div class="info-box error">The ZIP code you entered is incorrect. Please try again</div>');
          return;
        }

        geocoder.geocode( { 'address': address}, function(results, status) {
          if (status == google.maps.GeocoderStatus.OK) {

            clearLocations();

            var centerPoint = results[0].geometry.location,
                zip = {
                  lat: parseFloat(centerPoint.lat()),
                  lng: parseFloat(centerPoint.lng())
                };

            zip.slat = sphericize(zip.lat, 'lat');
            zip.slng = sphericize(zip.lng, 'lng');

            $.bounds = new google.maps.LatLngBounds();
            $.bounds.extend(centerPoint);

            // loop through locations, adding them if they're within [radius] of [address].
            var lastFound = {};

            for (var i=0; i < locsLength; i++) {
              var thisPosition = {
                slat: sphericize( parseFloat($.locs[i].lat), 'lat' ),
                slng: sphericize( parseFloat($.locs[i].lng), 'lng' )
              };
              var thisDistance = getDistance(zip, thisPosition);

              if (thisDistance < radius) {
                thisDistance = '<p class="distance">Within ' + thisDistance.toFixed() + ' miles of ' + address;
                addLocation(map, $.locs[i], thisDistance);

                lastFound = {
                  index: i,
                  distance: thisDistance
                };
              }
            }

            // now display the map at correct coords and zoom level
            map.panTo(centerPoint);
            map.fitBounds($.bounds);
            var zoomLevel = map.getZoom();

            if (zoomLevel > 9) {
              map.setZoom(zoomLevel-1);
            }

            if (markers.length === 1) {
              var locationInfo =  getLocationInfo($.locs[lastFound.index], lastFound.distance);
              openInfoWindow(locationInfo, markers[0]);
            } else if (markers.length === 0) {
              map.setZoom(7);
            }

            $('#zip-results').html(resultsSummary(markers.length, radius, address));

          // something went wrong with the geocoder
          } else if (window.console && console.log) {
            console.log("Geocode was not successful for the following reason: " + status);
          }
        });
      };

      function resultsSummary(num, rad, zip) {
        var topdiv = '<div class="info-box';
        var output = [
          '<div class="box">',
          'Found <b>' + num + '</b> dealer',
          (num === 1) ? ' ' : 's ',
          'within <b>' + rad + '</b> miles ' + 'of <b>' + zip + '</b>.'
        ].join('');

        if (num === 0) {
          var $rad = $('#radius'),
              next = rad == 50 ? 3 : $rad.find('option:selected').index() + 1;
          output += '<br/> Please try again';
          if (next < $rad.find('option').length) {
            output += ', perhaps within <b>' + $rad.find('option').eq(next).text() + '</b> instead';
          }
          output += '.';
          topdiv += ' error';
        }

        return topdiv + '">' + output + '</div>';
      }

      function getDistance(zip, near) {
        var distance = 3960 * acos( sin(zip.slat) * sin( near.slat) * cos(zip.slng - near.slng) + cos(zip.slat) * cos(near.slat) );
        return distance;
      }

      function sphericize(num, type) {
        if (type === 'lng') {
          return num * pi / 180;
        } else {
          return (90.0 - num) * pi / 180;
        }
      }



      // clear all locations from the map.
      function clearLocations() {
        $(document).trigger('closemaptip');

        for (var i = 0, ml = markers.length; i < ml; i++) {
          markers[i].setMap(null);
        }
        markers = [];
      }


      $('<form></form>', {
        id: 'zip-search',
        className: 'zip-search',
        html: formInputs.join(''),
        submit: fetchLocations
      }).insertBefore('#map');


    }
});


})(jQuery, Math);

