/**
 * The locator object is used to make store requests and populate the 
 * map with the results of that request 
 *	@author Karim Shehadeh
 *	@date 2/7/2009
 */

/**
 * The store object is used to keep track of the map marker
 * associated with a store and the store's data
 */
var Store = function(marker,data) {
	
	this.marker = marker;
	this.data = data;
    this.column = 0;
}

var locator = {};    

/** The Google Map object */
locator.map = null;    

/** The current center location of the map */
locator.lat = 37.0625;
locator.lng = -95.677068;

/** the brand ID the locator is restricting queries to */
locator.brand_id = 0;

/** This is where text-based feedback that needs to be given to the user will be placed */
locator.messageDivId = '';

/** The path to the disabled previous button image */
locator.previous_disabled = '';

/** The path to the enabled previous button image */
locator.previous_enabled = '';

/** The path to the disabled nextbutton image */
locator.next_disabled = '';

/** The path to the enabled next button image */
locator.next_enabled = '';

/** The id of the div containing the map */
locator.mapDivId = '';

/** The id of the div containing store listing */
locator.storesDivId = '';

/** The information about the last request that was made */
locator.lastRequest = null;

/** The full path to the root of the site (with ending slash) */
locator.base_url = '';

/** The path to the map marker icon on the server */
locator.map_marker = '';

/** Map icon array keyed on the type ID */
locator.icons = [];

/** An array of Store objects currently on the map */
locator.stores = [];

/** An array of columns for the store listing display */
locator.columns = [];

/** The column currently being viewed by the user */
locator.currentColumn = -1;

/** The last directions object created */
locator.directions = null;

/** This is the address given as the from address for the last directions or store location requested */
locator.from_address = '';

/** The last directions window created */
locator.directionsWindow = null;

/** The number of stores to show in each column */
locator.storesPerColumn = 3;

/** Indicate whether or not store results HTML should include the name of the mall
 * Note: The mall name is an extra text field that can be used for things other
 * than mall names
 */
locator.displayMallName = 0;

/**
 * Loads the map into the given div - this is an async call so do not 
 *  expect the map to be there upon return from the method.
 *  @param divId The ID of the Div.
 *  @param findClientLocation Set to true to try and figure out where the client is 
 */
locator.loadMap = function(divId,findClientLocation) {
   
    this.mapDivId = divId;
    
    var mapsLoaded = function() {

        locator.map = new google.maps.Map2(document.getElementById(locator.mapDivId));        
        locator.map.addControl(new GSmallMapControl());
		locator.map.addControl(new GMapTypeControl());

        var zoomLevel = 4;
        if (locator.from_address != '') {
            locator.getDirections(0,locator.from_address,'directions');
        }
        if (findClientLocation) {
            if (google.loader.ClientLocation != null) {
                locator.lat = google.loader.ClientLocation.latitude;
                locator.lng = google.loader.ClientLocation.longitude;
                zoomLevel = 13;
            }
            else {
                // recenter to the middle of the US.
                locator.lat = 38.410558;
                locator.lng = -96.152344;
            }
        }

        latLongOb = new GLatLng(locator.lat,locator.lng);
        locator.map.setCenter(latLongOb, zoomLevel);
        
        if (findClientLocation)
        {
            locator.requestStoresFilterByForm(locator.brand_id,latLongOb);
        }
    }
    
    google.load("maps", "2", {"callback" : mapsLoaded});
}                   

/**
 * Loads the map library from google but doesn't do anything else
 */
locator.loadMapLibrary = function() {

    var mapsLoaded = function() {
        
    }

    google.load("maps", "2", {"callback" : mapsLoaded});
}

/**
 * Called when a getLatLng map api call failed to find a matching address 
 */
locator.onRequestFailed = function () {
    $('#'+locator.messageDivId).html('<span class="error" style="padding:1px">Unable to find the address given</span>');
}

/** 
 * Called by the client form.  The following assumptions are made concerning
 *	the content of the criteria form: 1) The attribute fields have the class "store_attribute"
 *	2) The type fields have the class "type_attribute"
 *	3) The address field has the id "address"
 *	4) The distance field has the id "distance"
 *	@param brandId The id of the brand currently be displayed to the user
 *	@param sourceAddress Pass an empty string to use the form's source address. Otherwise
 *          you can pass in an address string or a GLatLng object.
 */
locator.requestStoresFilterByForm = function (brandId,sourceAddress) {
	var storeAttributes = [];
	$('input.store_attribute:checked').each(function(i,el){storeAttributes.push(el.value);});

	var storeTypes = [];
	$('input.store_type:checked').each(function(i,el){storeTypes.push(el.value);});

    var address = '';
    if (sourceAddress == '')
    {
        address = $('#address').val();
    }
    else
    {
        address = sourceAddress;
    }
	
	var distance = $('#distance').val();
	
	this.requestStoresFilterByParams (brandId,storeTypes,storeAttributes,address,distance);
}

/**
 * Gets the cached GIcon object for the given typeId or creates a new one 
 *	and adds it to the cache
 *	@param typeId The id of the type
 *	@return {Object} Returns the GIcon object
 */
locator.getStoreIcon = function(typeId)
{
    // The typeid is ignored for now since the icon is the same for all stores
    //  that are part of the same brand no matter what the type is.  This
    //  system is being left in place for now in case we decide to change
    //  the way we do things.
	if (!locator.icons[0]) {
        
		var icon = new GIcon();
		
		icon.image = locator.map_marker;
		icon.iconAnchor = new GPoint(16, 16);
		icon.infoWindowAnchor = new GPoint(16, 0);
		icon.iconSize = new GSize(32, 32);		
		locator.icons[0] = icon;
	}
	
	return locator.icons[0];
}

locator.addStoreToInternalList = function(store) {
    locator.stores.push(store);
}

/**
 * Adds the store to the map 
 * @param {Object} store The store object to add (returned by the server as JSON)
 */
locator.addStoreToMap = function(store) {
	
	var marker = new GMarker(new GLatLng(store.latitude,store.longitude), locator.getStoreIcon(store.type_id));
	
	if (marker != null) {	
		
        var storeOb = new Store(marker,store);
        locator.map.addOverlay(marker);    		
		locator.addStoreToInternalList(storeOb);
        
		GEvent.addListener(marker,"click", function() {
		        locator.openStoreInfoWindow(storeOb);
                locator.scrollToStoreInStoreList(storeOb);
		      });				
	}
}

/**
 * Finds the store in the store list, makes sure it's visible and
 * then highlights that store.
 * @param {Object} storeOb The store object to scroll to
 */
locator.scrollToStoreInStoreList = function(storeOb) {

    if (storeOb.column > -1) {

        locator.showColumn(storeOb.column);

        // Now remove any previous highlights and highlight the new one
        $('.store').removeClass('highlight');
        $('#store_'+storeOb.data.store_id).addClass('highlight');
    }
}

/**
 * Takes all the criteria for requesting stores as parameters to the 
 *	function (as opposed to getting from a form automatically).  
 *	@param brandId The id of the brand
 *	@param storeTypes An array of store type ids
 *	@param storeAttributes An array of store attributes
 *	@param sourceAddress The address to find as a string or GLatLng object
 *  @param distance The radius in miles around the given address to search for stores
 */
locator.requestStoresFilterByParams = function(brandId,storeTypes,storeAttributes,sourceAddress,distance) {

	locator.clearStores();

    locator.from_address = sourceAddress;
    
    // Store the request first
	this.lastRequest = {
		'brand':brandId,
		'types':storeTypes,
		'attributes':storeAttributes,
		'address':sourceAddress,
		'distance':distance
	}


    var requestStoreData = function (point) {
        
        typesAsString = storeTypes.join(',');
        attributesAsString = storeAttributes.join(',');

        if (point)
        $.getJSON(locator.base_url + 'index.php/rest/search/',
                    {b:brandId,
                     t:typesAsString,
                     a:attributesAsString,
                     lat:point.lat(),
                     lng:point.lng(),
                     dst:distance},
                     locator.handleStoreRequestResults);

    }


	// Now convert the given address to a lat/long
    if (typeof sourceAddress == 'object')
    {
        requestStoreData(sourceAddress);
    }
    else
    {
        var geocoder = new GClientGeocoder();
        geocoder.getLatLng(sourceAddress,function(point) {
            if (!point) {
                locator.onRequestFailed();
            }
            else {
                $('#'+locator.messageDivId).html('Enter Your City, State OR Your ZIP Code');
                requestStoreData(point);
            }
        });
    }
}

/**
 * Called when the server returns a set of stores as a JSON object
 *	@param {Object} data The JSON data returned from the array converted to 
 *			a javascript object.
 */
locator.handleStoreRequestResults = function(data) {
   
	for (i=0;i<data.length;i++) {			
		var store = data[i];
		locator.addStoreToMap(store);
		
		if (i == 0) {
			locator.map.setCenter(new GLatLng(store.latitude, store.longitude), 13);
		}
	}
	
	// Now update the store listing
	locator.buildStoreListColumns();
}

locator.clearStores = function() {
	
	for (i=0;i < locator.stores.length; i++) {
		var m = locator.stores[i].marker;
		if (m != null) {
			locator.map.removeOverlay(m);
			locator.stores[i].data = null;
			locator.stores[i].marker = null;
		}
	}
	
	locator.stores = [];
}

/**
 * Finds the given store on the map and recenters the map on that 
 *	location.  It will optionally show the info window associated with
 *	the store if showInfoWindow is set to true
 *	@param {int} storeIndex The index of the store in the stores array
 *	@param {bool} showInfoWindow Set to true to show the info window
 *					after the map has been recentered.
 */
locator.findStoreOnMap = function(storeIndex,showInfoWindow) {
	
	if (storeIndex < 0 || storeIndex >= locator.stores.length) {
		// invalid store index given
		return false;
	}
	
	var store = locator.stores[storeIndex];
	if (store != null) {
		
		locator.map.setCenter(store.marker.getLatLng());
		
		if (showInfoWindow) {
			locator.openStoreInfoWindow(store);
		}
	}

    return true;
}

/**
 * This will show the info window for a given store object.  It does
 *	does not automatically center the map on that location, though.
 *	@param {Object} store The 'Store' object containing information about the store
 *			and the marker associated with it.
 */
locator.openStoreInfoWindow = function (store) {
	var htmlContent = "<b>" + store.data.name + 
						"</b><br/><address>" + store.data.address + 
						"</address><br/><b>Phone:&nbsp;</b>" + 
						store.data.phone_number + "<br/>"+locator.getDirectionsLink(store,locator.from_address);
						
    store.marker.openInfoWindowHtml(htmlContent);	
}

/**
 * Shows the column of stores chosen by the user 
 */
locator.showColumn = function (columnIndex) {

    if ((locator.currentColumn != columnIndex) &&
        ((columnIndex >= 0) && (columnIndex < locator.columns.length))) {

        var oldSlideDirection = locator.currentColumn > columnIndex ? "right" : "left";
        var newSlideDirection = locator.currentColumn > columnIndex ? "left" : "right";
        
        if (locator.currentColumn >= 0) {

            if (locator.currentColumn >= 0 && (locator.currentColumn < locator.columns.length)) {
                locator.columns[this.currentColumn].hide('slide',{direction:oldSlideDirection},1000);
            }
        }

        locator.currentColumn = columnIndex;

        locator.columns[columnIndex].show('slide',{direction:newSlideDirection},1000);

        // Now update the next/previous links
        if (columnIndex > 0) {
            $('.previous').css('background-image','url('+locator.previous_enabled+')');
        }
        else {
            $('.previous').css('background-image','url('+locator.previous_disabled+')');
        }

        if (columnIndex < (locator.columns.length - 1)) {
            $('.next').css('background-image','url('+locator.next_enabled+')');
        }
        else {
            $('.next').css('background-image','url('+locator.next_disabled+')');
        }

        var storeRangeLower = (columnIndex * locator.storesPerColumn) + 1;
        var storeRangeUpper = storeRangeLower + (locator.getNumberOfStoresInColumn(columnIndex)-1);
        var totalStores = locator.stores.length;

        if (totalStores <= locator.storesPerColumn)
        {
            $('#stores_header').html("<span class=\"stores_found_number\">" + locator.stores.length + "</span> stores were found");
        }
        else
        {
            $('#stores_header').html("Showing <span class=\"stores_found_number\">"+storeRangeLower+"</span> - <span class=\"stores_found_number\">" + storeRangeUpper + "</span> of <span class=\"stores_found_number\">" + totalStores + "</span>");
        }        
    }
    else if (locator.stores.length == 0)
    {
        $('#stores_header').html("<span class=\"stores_found_number\">0</span> stores were found");
    }
}

/**
 * Shows the next available column (if there is no next then nothing happens)
 */
locator.nextStoreColumn = function() {
	locator.showColumn(this.currentColumn+1);
}

/**
 * Shows the previous column.  If there is no previous then nothing happens.
 */
locator.prevStoreColumn = function() {
	locator.showColumn(this.currentColumn-1);
}

/**
 * Determines how many stores are in a particular column
 * @param columnIndex The zero-based index of the column
 * @return {int} The number of stores in the column
 */
locator.getNumberOfStoresInColumn = function(columnIndex) {

    var totalStores = 0;
    for (i = 0 ; i < locator.stores.length ; i++) {

        if (locator.stores[i].column == columnIndex) {
            totalStores++;
        }
    }

    return totalStores;
}

/**
 * Builds the columns sets associated with the results.  Each columns a
 * maximum number of stores.  This will also generate the 'next' 'previous'
 * links as necessary.  Any previous listings will be destroyed
 */
locator.buildStoreListColumns = function() {
		
	var buildColumnIndex = 0;
	var col = null;
	locator.currentColumn = -1;
	
	// remove previous stores
	$('#stores').html('');
	
	// Remove all column nodes from memory (?)
	for(j=0;j < locator.columns.length;j++) {
		locator.columns[j] = null;
	}
	locator.columns = [];
	
	for (i = 0; i < locator.stores.length; i++) {

		var store = locator.stores[i];        
		if (i % locator.storesPerColumn == 0) {
					
			// make a new column
			col = jQuery('<div></div>').addClass('store_column').attr('id','column_'+buildColumnIndex).hide();
			locator.columns[buildColumnIndex] = col;
			$('#stores').append(col);
			
			buildColumnIndex++;			
		}

        store.column = locator.columns.length-1;
		
		var storeDiv = jQuery('<div></div>').attr('id','store_'+store.data.store_id).addClass('store');
		storeDiv.append(jQuery('<b>'+store.data.name+'</b>'));
		storeDiv.append(jQuery('<address>'+store.data.address+'</address>'));
		storeDiv.append(jQuery('<div class="phone">'+store.data.phone_number+'</div>'));

        if (locator.displayMallName) {
         	storeDiv.append(jQuery('<div class="extra_1">'+store.data.mall_name+'</div>'));
        }
        
        //storeDiv.append(jQuery('<div class="distance">'+store.data.distance+' miles away</div>'));
		storeDiv.append(jQuery('<a class="mapfind" href="#" onclick="locator.findStoreOnMap('+i+',true)">Find on map</a>'));
		storeDiv.append(jQuery(locator.getDirectionsLink(store,locator.from_address)));
		col.append(storeDiv);
	}

    if (locator.stores.length == 0) {
        $('#stores').html('<div style="padding:10px;">No stores were found.  Try selecting a larger radius, shop online, or call customer service.</div>');
    }
    
	locator.showColumn(0);	
}

locator.getDirectionsLink = function(store,defaultFromAddress) {

    var link = locator.base_url + 'index.php/locator/directions/?s=' + store.data.store_id + '&f='+escape(defaultFromAddress);
    return '<a class="directions" href="'+ link +'">Get Directions</a>';
}

locator.getDirections = function(storeIndex,from,destinationDiv) {    
        
    if (storeIndex < 0 || storeIndex >= locator.stores.length) {
        // invalid store index given
        return false;
    }        
    var store = locator.stores[storeIndex];

    var destinationDivOb = document.getElementById(destinationDiv);
    destinationDivOb.innerHTML = '';

    locator.from_address = from;

    if (locator.directions == null) {
        locator.directions = new GDirections(locator.map,destinationDivOb);
    }
    else {
        locator.directions.clear();
    }
    
    if (locator.directions != null) {
        var queryString = 'from: ' + from;
        queryString += ' to: ' + store.data.address;

        locator.directions.load(queryString);
    }
       
    return true;
}

/**
 * Display the UI to request an email address from the user then
 * sends the email to that email address.
 * @param {string} emailAddress The email address to send to
 * @param {emailCallback} emailCallback The function call when the server replies
 */
locator.emailCurrentDirections = function(emailAddress,emailCallback) {

    var route = locator.directions.getRoute(0);
    var stepCount = route.getNumSteps();
    var steps = [];
    for(i=0;i<stepCount;i++){
        var step = route.getStep(i);
        steps.push({description:step.getDescriptionHtml(),
                    distance:step.getDistance(),
                    duration:step.getDuration()});    
    }

    var jsonSteps = JSON.stringify(steps);
    var jsonStore = JSON.stringify(locator.stores[0].data);

    $.post(locator.base_url+'index.php/rest/emaildir',
         {
             email:emailAddress,
             steps:jsonSteps,
             store:jsonStore,
             from:locator.from_address
         },

         emailCallback, "json");
}

/**
 * Display the UI to request an email address from the user then
 * sends the email to that email address.
 */
locator.printCurrentDirections = function(containerDiv) {

    var doc = document.getElementById(containerDiv);
    var win = window.open('', "TrackHistoryData",
                          "width=740,height=325,top=200,left=250,toolbars=no,scrollbars=yes,status=no,resizable=no");
    win.document.writeln(doc.innerHTML);
    win.document.close();
    win.focus();
    win.print();
    win.close();
}

/**
 * Displays the directions window setup with the correct store. This will
 * use an existing window if one was already created
 *  @param {int} storeIndex The index into the store array of the store to 
 *          display directions to
 *  @param {string} defaultFromAddress The address to display in the from field by default.
 */
locator.showDirections = function(storeIndex,defaultFromAddress) {
	
	if (storeIndex < 0 || storeIndex >= locator.stores.length) {
		// invalid store index given
		return false;
	}
	
    var store = locator.stores[storeIndex];        
    var link = locator.base_url + 'index.php/locator/directions/?s=' + store.data.store_id + '&f='+escape(defaultFromAddress);
    
    locator.directionsWindow = window.open(link,"Directions to " + store.data.name,
        "menubar=1,toolbar=0,resizable=1,status=0,scrollbars=1,width=948,height=810");
    
    if (window.focus) {
        locator.directionsWindow.focus()
    }

    return true;
}