/**
 * Routes RunTrackr Map.
 *
 * Defines the map used for displaying single routes and associated data.
 *
 * @class RouteMap
 * @namespace runtrackr
 * @requires runtrackr.Map, PolylineEncoder
 */

// TODO: Should the distance units setting be a part of RouteMap, or
// completely separate? That is, getDistance() ONLY returns in KM, up to
// the caller to convert? This may result in REPEATED code when used to
// display distance settings in different areas... (i.e. storing the current
// units and calculations)

/**
 * Constructor must take the id of the element to use as the map, along with
 * optional parameters specifying the initial map state.
 *
 * @param Object options an object containing the following properties:
 *        mapId: (String) the id of the DOM element to use as the map.
 *        centerPoint: (GLatLng) the optional initial location to center on.
 *        initialZoom: (Number) the optional initial zoom level of the map.
 *        saveUrl: (String) the URL to direct Ajax save requests to.  Not
 *          necessary if the route will not be made editable.
 */
runtrackr.RouteMap = function(options)
{
  // Invoke constructor in superclass.
  runtrackr.Map.apply(this, arguments);

  // Disable double-click for zoom since editing.
  this._map.disableDoubleClickZoom();

  // URL to send Ajax POST requests to for saving routes.
  if (options.saveUrl)
  {
    this._saveUrl = options.saveUrl;
  }
  else
  {
    this._saveUrl = '';
  }

  // Callback invoked after the route has been updated/changed state.
  if (typeof options.updateRouteCallback == 'function')
  {
    this._updateRouteCallback = options.updateRouteCallback;
  }
}
runtrackr.RouteMap.prototype = new runtrackr.Map();

/*--- Public constants ---*/

// TODO: Should these icons be part of the runtrackr.Map package?
/**
 * Base icon type.
 *
 * @type GIcon
 * @final
 */
runtrackr.RouteMap.ICONS = {};
runtrackr.RouteMap.ICONS.base = new GIcon();
runtrackr.RouteMap.ICONS.base.iconSize=new GSize(32,32);
runtrackr.RouteMap.ICONS.base.shadowSize=new GSize(56,32);
runtrackr.RouteMap.ICONS.base.iconAnchor=new GPoint(16,32);
runtrackr.RouteMap.ICONS.base.infoWindowAnchor=new GPoint(16,0);

/**
 * Standard marker icon.
 *
 * @type GIcon
 * @final
 */
runtrackr.RouteMap.ICONS.standard = new GIcon(
  runtrackr.RouteMap.ICONS.base,
  "http://maps.google.com/mapfiles/kml/pal3/icon61.png",
  null,
  "http://maps.google.com/mapfiles/kml/pal3/icon61s.png"
);
runtrackr.RouteMap.ICONS.standard.iconAnchor=new GPoint(16,16);

/**
 * Start marker icon.
 *
 * @type GIcon
 * @final
 */
runtrackr.RouteMap.ICONS.start = new GIcon(
  runtrackr.RouteMap.ICONS.base,
  "http://www.google.com/mapfiles/dd-start.png"
);
runtrackr.RouteMap.ICONS.start.iconSize = new GSize(20, 34);
runtrackr.RouteMap.ICONS.start.iconAnchor = new GPoint(10, 34);

/**
 * Finish marker icon.
 *
 * @type GIcon
 * @final
 */
runtrackr.RouteMap.ICONS.finish = new GIcon(
  runtrackr.RouteMap.ICONS.base,
  "http://www.google.com/mapfiles/dd-end.png"
);
runtrackr.RouteMap.ICONS.finish.iconSize = new GSize(20, 34);
runtrackr.RouteMap.ICONS.finish.iconAnchor = new GPoint(10, 34);

/*--- Private variables ---*/

/**
 * Polyline defaults.
 *
 * @private
 * @static
 */
runtrackr.RouteMap._POLYLINE =
{
  COLOR: '#ff0000',
  WIDTH: 3
};

/**
 * Polyline encoder options.
 *
 * @private
 * @static
 */
runtrackr.RouteMap._POLYLINE.ENCODER =
{
  NUM_LEVELS: 18,
  ZOOM_FACTOR: 2,
  VERY_SMALL: 0.00001
}

/**
 * Messages presented to the user.
 */
runtrackr.RouteMap._MESSAGES =
{
  DEFAULT_EDITING_DISABLED: 'Route editing is currently disabled.'
};

/**
 * Error messages.
 */
runtrackr.RouteMap._MESSAGES.ERROR =
{
  ROUTE_NOT_ENOUGH_POINTS: 'Please enter two or more points before saving your route.',
  ROUTE_NAME_EMPTY: 'Please enter a name for your route.'
}

/**
 * Tooltip messages.
 */
runtrackr.RouteMap._MESSAGES.TOOLTIP =
{
  START_MARKER : 'Start',
  FINISH_MARKER: 'Finish'
}

/**
 * Whether the route has been modified from its original state.
 *
 * @type Boolean
 * @private
 */
runtrackr.RouteMap.prototype._isRouteModified = false;

/**
 * Whether route editing is enabled.
 *
 * @type Boolean
 * @private
 */
runtrackr.RouteMap.prototype._isEditingEnabled = false;

/**
 * Stores event handlers that make the route editable so that they may
 * be unregistered later to make the route read-only.
 *
 * @type Array<GEventListener>
 * @private
 */
runtrackr.RouteMap.prototype._editingEventHandlers = [];

/**
 * The message to display when route editing is disabled.
 */
runtrackr.RouteMap.prototype._disabledMessage = runtrackr.RouteMap._MESSAGES.DEFAULT_EDITING_DISABLED;

/**
 * The event handler for map clicks when route editing is disabled.
 */
runtrackr.RouteMap.prototype._disabledEventHandler;

/**
 * The polyline representing the route.
 *
 * @type GPolyline
 * @private
 */
runtrackr.RouteMap.prototype._route = new GPolyline();

/**
 * Polyline encoder.
 *
 * @type PolylineEncoder
 * @private
 */
runtrackr.RouteMap.prototype._polylineEncoder = new PolylineEncoder(
  runtrackr.RouteMap._POLYLINE.ENCODER.NUM_LEVELS,
  runtrackr.RouteMap._POLYLINE.ENCODER.ZOOM_FACTOR,
  runtrackr.RouteMap._POLYLINE.ENCODER.VERY_SMALL
);

/*--- Methods ---*/

/**
 * Attempts to set the map location to the supplied location.
 *
 * If a single, unambiguous location cannot be found, (via Geocoding) the user
 * is prompted to narrow the search or try again.
 *
 * @param String locationValue the location to set the map to.
 * @param Object callback the callback functions {success, failure} to invoke
 *        after the request has completed.
 */
runtrackr.RouteMap.prototype.setLocation = function(locationValue, callback)
{
  // Used by the callback to gain access to the instance object by forming
  // a closure.
  var this_ = this;
  this._geocoder.getLocations(
    locationValue,
    function(response)
    {
      // Call the actual Geocoder callback function with the proper context.
      this_._setLocationGeocoderCallback(response, callback);
    }
  );
}

/**
 * Sets the map location based on the supplied Placemark object.
 *
 * The Placemark object should be one of the entries of the JSON `Placemark`
 * object returned by a geocoder response.
 *
 * @param Object placemark the Placemark object.
 */
runtrackr.RouteMap.prototype.setLocationPlacemark = function(placemark)
{
  // Extract the coordinates and build a GLatLng point.
  var point = new GLatLng(
    placemark.Point.coordinates[1],
    placemark.Point.coordinates[0]
  );

  var accuracy = placemark.AddressDetails.Accuracy;

  // Set zoom level based on accuracy returned.
  var zoomLevel = this._getZoomLevel(placemark.AddressDetails.Accuracy);

  // Clear the current route off the map and recenter.
  this.clearRoute();
  this._map.setCenter(point, zoomLevel);

  // If the specified location was accurate enough, place a starting point there.
  if (accuracy >= runtrackr.Map.GGeoAddressAccuracy.INTERSECTION)
  {
    this._mapClick(null, point);
  }
}

/**
 * Callback function to deal with the geocoder response.
 *
 * @param Object response the geocoder response containing a list of places.
 * @param Object callback the callback functions. ({success, failure}).
 * @private
 */
runtrackr.RouteMap.prototype._setLocationGeocoderCallback = function(response, callback)
{
  // TODO: Use try-catch to clean up the flow...

  if (response.Status.code == G_GEO_SUCCESS)
  {
    // Geocoder was able to resolve the location to a lat/lng.
    if (response.Placemark.length == 1)
    {
      // Ensure that there was one unique location returned.
      var place = response.Placemark[0];

      // TODO: This variable no longer needed?
      // Extract the coordinates and build a GLatLng point.
      var point = new GLatLng(
        place.Point.coordinates[1],
        place.Point.coordinates[0]
      );

      var accuracy = place.AddressDetails.Accuracy;

      // Check if city-level accuracy was returned from the geocoding call.
      if (accuracy >= runtrackr.Map.GGeoAddressAccuracy.LOCALITY)
      {
        // Set the map state to the new location.
        this.setLocationPlacemark(place);

        // Invoke success callback with the placemark from the geocoder response.
        if (typeof callback.success == 'function')
          callback.success(response.Placemark[0]);
      }
      else
      {
        // TODO: Figure out how to handle less-than-locality accuracy.
        if (typeof callback.failure == 'function')
          callback.failure(runtrackr.Map.ERROR.LOCATION_NOT_ACCURATE);
      }
    }
    else
    {
      // TODO: May want to have a separate property/function specifically to
      // deal with this to avoid having if/else in the failure callback.
      // - Would remove the need to use errorCodes.

      // Multiple matches found for the query; pass the list of matches back
      // to the callback to process.
      if (typeof callback.failure == 'function')
      {
        callback.failure(runtrackr.Map.ERROR.LOCATION_MULTIPLE_MATCHES,
          response.Placemark
        );
      }
    }
  }
  else
  {
    // TODO: Figure out what to do if not found.
    if (typeof callback.failure == 'function')
      callback.failure(runtrackr.Map.ERROR.LOCATION_NOT_FOUND);
  }
}

/**
 * Gets the map zoom level associated with a given accuracy returned from the
 * geocoder.
 *
 * This specifies the recommended zoom level to set the map to for a given
 * accuracy response from the geocoder.  For example, the zoom level is
 * higher for street-level accuracy than for country-level accuracy.
 *
 * @param Number accuracy the accuracy level, as defined in
 * {@link runtrackr.Map.GGeoAddressAccuracy}
 * @return the zoom level corresponding to the supplied accuracy.
 * @private
 */
runtrackr.RouteMap.prototype._getZoomLevel = function(accuracy)
{
  var zoomLevel = runtrackr.Map.ZOOM_DEFAULTS.LOCALITY;

  if (accuracy == runtrackr.Map.GGeoAddressAccuracy.ADDRESS)
  {
    // TODO: Also set the first point at this location?
    // Pass back accuracy or check if zoom level adequate in callback, which
    // can then take the action.
    zoomLevel = runtrackr.Map.ZOOM_DEFAULTS.ADDRESS;
  }
  else if (accuracy >= runtrackr.Map.GGeoAddressAccuracy.STREET)
  {
    zoomLevel = runtrackr.Map.ZOOM_DEFAULTS.STREET;
  }

  return zoomLevel;
}

/**
 * Enables the route to be edited.
 */
runtrackr.RouteMap.prototype.enableEditing = function()
{
  // Remove the disabled editing map-click handler if present.
  if (this._disabledEventHandler)
  {
    GEvent.removeListener(this._disabledEventHandler);
    this._disabledEventHandler = null;
  }

  if (!this._isEditingEnabled)
  {
    this._isEditingEnabled = true;

    // Register event handlers and keep track of them so they can be removed
    // if editing needs to be disabled.
    this._editingEventHandlers.push(
      GEvent.bind(this._map, 'click', this, this._mapClick)
    );
  }
}

/**
 * Disables editing of the route.
 */
runtrackr.RouteMap.prototype.disableEditing = function(reason)
{
  // TODO: Problem - markers may still have event handlers - drag/move -
  // attached to them.  Can fix by not allowing dragging or disabling
  // drag for each individual marker and then re-enabling...

  // TODO: Possibly remove all markers but keep track of encoded points...
  // then can re-enable by adding markers all back when using
  // enableEditing().

  // Remove the event handlers related to route editing.
  var handle = this._editingEventHandlers.pop()
  while (handle)
  {
    GEvent.removeListener(handle);
    handle = this._editingEventHandlers.pop();
  }

  // Set the disabled reason, which is shown to the user when trying to edit.
  if (reason)
  {
    this._disabledMessage = reason;
  }
  else
  {
    this._disabledMessage = runtrackr.RouteMap._MESSAGES.DEFAULT_EDITING_DISABLED;
  }

  // Bind the notification event.
  this._disabledEventHandler = GEvent.bind(
    this._map, 'click', this, this._mapClickEditingDisabled);
}

/**
 * Event handlers for map clicks.
 *
 * @param GOverlay overlay set if a map overlay was clicked.
 * @param GLatLng point if an overlay was NOT clicked, this will be set with
 *        the lat/lng corresponding to the point clicked on the map.
 * @private
 */
runtrackr.RouteMap.prototype._mapClick = function(overlay, point)
{
  if (point)
  {
    // Add the point to the map as a GMarker overlay.

    // Do not add the point unless sufficiently zoomed in.
    if (this._map.getZoom() < runtrackr.Map.ZOOM_DEFAULTS.MIN_ADDING_POINTS)
    {
      this._notify('Please zoom in more before trying to add points.');
      return;
    }

    // Create and add the marker, update the route and then center on the
    // new marker/point.
    var marker = this._addMarker(point);
    this._updateRoute();
    this._map.panTo(marker.getPoint());
  }
}

/**
 * Event handler used when route editing is disabled and the map is clicked.
 *
 * @private
 */
runtrackr.RouteMap.prototype._mapClickEditingDisabled = function()
{
  window.alert(this._disabledMessage);
}

/**
 * Adds a marker to the specified point on the map, and registers the proper
 * event handlers to make the marker interactive.
 *
 * The route on the map (GPolyline) is not updated since this method may be
 * called multiple times in a loop when loading in data.  In that case, the
 * caller would only want to update the route ONCE after all points had been
 * loaded, for performance reasons.
 *
 * The marker is returned so that the caller may have access to it.
 *
 * @param GLatLng the point to add.
 * @return GMarker the marker just added to the map.
 * @private
 */
runtrackr.RouteMap.prototype._addMarker = function(point)
{
  // Define the icon for the marker.
  var icon = (0 == this._markers.length) ? runtrackr.RouteMap.ICONS.start : runtrackr.RouteMap.ICONS.standard;

  // Create the marker.
  var marker = new GMarker(point, {'icon' : icon, 'draggable' : true});

  // Add event handler to update route after a marker is dragged/moved.
  GEvent.bind(marker, 'dragend', this, this._updateRoute);

  // Assign the marker to the list of markers.
  this._markers.push(marker);

  // Add the marker to the map.
  this._map.addOverlay(marker);

  // Return the GMarker object so that the caller can use it.
  return marker;
}

/**
 * Removes the last marker from the map, if at least one marker exists.
 *
 * @return GMarker the marker just removed, provided there was one to remove.
 *         Otherwise, `undefined` is returned.
 */
runtrackr.RouteMap.prototype.removeLastMarker = function()
{
  // Remove the marker from the map and from the list of markers.
  var markerToBeRemoved = this._markers.pop();
  if (markerToBeRemoved)
  {
    this._map.removeOverlay(markerToBeRemoved);
    this._updateRoute();

    // Center on the previous point, if available.
    var previousPointIndex = this._markers.length - 1;
    if (previousPointIndex >= 0)
    {
      this._map.panTo(this._markers[previousPointIndex].getPoint());
    }

    return markerToBeRemoved;
  }
}

/**
 * Clears the current route from the map.
 */
runtrackr.RouteMap.prototype.clearRoute = function()
{
  // Remove all markers from the map.
  this._map.clearOverlays();

  // Clear the list of markers.
  this._markers = [];

  // Update route state on the map.
  this._updateRoute();
}

/**
 * Updates the route and other resources on screen after the state has changed.
 *
 * @private
 */
runtrackr.RouteMap.prototype._updateRoute = function()
{
  // Build the list of points from the list of markers.
  var points = this._getPoints();

  // Remove the previous route overlay and update the map with the new one.
  this._map.removeOverlay(this._route);
  this._route = new GPolyline(points, runtrackr.RouteMap._POLYLINE.COLOR, runtrackr.RouteMap._POLYLINE.WIDTH);
  this._map.addOverlay(this._route);

  // Set the map state to modified.
  this._isRouteModified = true;

  // Invoke callback after route has been updated.
  this._updateRouteCallback();
}

/**
 * Placeholder callback function in case the callback is not specified in the
 * constructor.
 *
 * This function is invoked whent the route/map state has changed.  It should
 * be used to update other route information on the screen that isn't already
 * handled by the internal updateRoute() function, i.e. the current route
 * distance, calories, etc.  This is to avoid tying RouteMap to any specific
 * DOM elements outside of the map itself.
 *
 * @private
 */
runtrackr.RouteMap.prototype._updateRouteCallback = function() {}

/**
 * Gets the total length of the route in kilometers.
 *
 * @return Number the distance of the route in kilometers.
 */
runtrackr.RouteMap.prototype.getRouteDistance = function()
{
  // GPolyline.getLength() returns distance in meters.
  var distance = this._route.getLength()/1000;
  if ('' == distance)
  {
    distance = 0;
  }

  return distance;
}

/**
 * Saves the route and associated data to the server.
 *
 * @param Object additionalRouteData additional data, in the form of key-value
 *        pairs, to send to the server along with the route.
 * @param Object callback the {success, failure} callbacks.
 */
runtrackr.RouteMap.prototype.saveRoute = function(additionalRouteData, callback)
{
  /*
   * TODO:
   * 1) If don't have at least city/country, may have to prompt before saving.
   * 2) All data should be checked server-side, but to be friendly, should
   * check here as well.  Initially, DO NOT check unless have to; should be
   * all server-side.
   *
   * 3) Either use a modal-block or disable saving buttons while operation
   * is taking place.
   */

  // TODO: If a route is edited, either put a notification on screen OR
  // prompt the user when they try to navigate away from a changed/edited
  // route that hasn't been saved.

  // Get the ordered list of points.
  var points = this._getPoints();

  // Make sure there are at least two points in the route, otherwise do not
  // save the route.
  if (points.length < 2)
  {
    this._notify(runtrackr.RouteMap._MESSAGES.ERROR.ROUTE_NOT_ENOUGH_POINTS);
    return;
  }

  // Convert points to an encoded format.
  // NOTE: ASCII range of encoded points: [63-126] inclusive.  Includes
  // backslash ('\') but NOT any quotes.
  var enc = this._polylineEncoder.dpEncode(points);

  // Form the route data that needs to be saved.  Note that the starting point's
  // coordinates are saved separately, allowing the location of the route to be
  // determine independently of the encoded points.
  var dataToBeSaved =
  {
    'data[Route][encoded_points]' : enc.encodedPoints,
    'data[Route][encoded_points_literal]' : enc.encodedPointsLiteral,
    'data[Route][encoded_levels]' : enc.encodedLevels,
    'data[Route][num_points]' : points.length,
    'data[Route][lat]' : points[0].lat(),
    'data[Route][lng]' : points[0].lng(),
    'data[Route][distance]' : this.getRouteDistance()
  }

  // Make sure the additionalRouteData is at least an object, which may be an
  // array.
  if (typeof additionalRouteData == 'object')
  {
    // Add the additional data.  Note that Array.concat(anotherArray) may not
    // work in this case since we aren't dealing with actual Array objects.
    for (var key in additionalRouteData)
    {
      // Do not overwrite existing fields.
      if (!dataToBeSaved[key])
      {
        dataToBeSaved[key] = additionalRouteData[key];
      }
    }
  }

  // Make sure name is non-empty.
  if (!dataToBeSaved['data[Route][name]'] || jQuery.trim(dataToBeSaved['data[Route][name]']).length == 0)
  {
    this._notify(runtrackr.RouteMap._MESSAGES.ERROR.ROUTE_NAME_EMPTY);
    return;
  }

  /*--- Submit the data to the server, Ajax-POST style. ---*/
  // jQuery's 'processData' option processes the 'data' object into a proper
  // query string, encoded to conform to the "application/x-www-form-urlencoded"
  // content type.

  // ALSO: Should disable/re-enable editing during and after this operation.
  // Otherwise, unexpected behaviour may result.
  // ALSO: Disable the save buttons, re-enable afterwards - nothing to prevent
  // a malicious user/client from submitting multiple times but should prevent
  // for user-friendly.
  jQuery.ajax(
  {
    'type' : 'POST',
    'url' : this._saveUrl,
    'processData' : true,
    'data' : dataToBeSaved,
    'dataType' : 'json',
    'timeout' : runtrackr.AJAX_TIMEOUT,
    'success' : callback.success,
    'error' : callback.failure
  });
}

/**
 * Loads a route from a supplied list of points.
 *
 * This method should be supplied with an object containing the encoded points
 * and their levels.
 *
 * @param Object routeData an object containing the following properties:
 *        encodedPoints: a string containing the encoded points.
 *        encodedLevels: a string containing the encoded levels.
 *        editable: an optional boolean property to specify whether the
 *                  resulting route should be editable or not.  Defaults to
 *                  false.
 */
runtrackr.RouteMap.prototype.loadRoute = function(routeData)
{
  // TODO: Should set the placemark-location related hidden inputs when
  // loading a route. (Use a callback)

  // TODO: Be prepared with error message if supplied encoded data is not
  // valid. (How to tell?)
  this._map.removeOverlay(this._route);
  // TODO: Should this be `new GPolyline.fromEncoded()`???
  // Ref: http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/example1.js
  // http://code.google.com/apis/maps/documentation/overlays.html#Polylines_Overview
  // Though it seems to be working here...
  this._route = GPolyline.fromEncoded(
  {
   'color' : runtrackr.RouteMap._POLYLINE.COLOR,
   'weight' : runtrackr.RouteMap._POLYLINE.WIDTH,
   'points' : routeData.encodedPoints,
   'levels' : routeData.encodedLevels,
   'zoomFactor' : runtrackr.RouteMap._POLYLINE.ENCODER.ZOOM_FACTOR,
   'numLevels' : runtrackr.RouteMap._POLYLINE.ENCODER.NUM_LEVELS
  });

  // Add the corresponding markers if the route is editable.  The GPolyline
  // representing the route is added by the call to updateRoute().
  if (routeData.editable)
  {
    var nPoints = this._route.getVertexCount();
    for (var i = 0; i < nPoints; ++i)
    {
      this._addMarker(this._route.getVertex(i));
    }
    this._updateRoute();
  }
  else
  {
    // If map is not editable, do not add all markers; only the start/finish.
    // The GPolyline representing the route will still be visible.
    this._map.addOverlay(this._route);

    var startMarker = new GMarker(
      this._route.getVertex(0),
      {
        'icon' : runtrackr.RouteMap.ICONS.start,
        'title' : runtrackr.RouteMap._MESSAGES.TOOLTIP.START_MARKER
      }
    );
    var finishMarker = new GMarker(
      this._route.getVertex(this._route.getVertexCount() - 1),
      {
        'icon' : runtrackr.RouteMap.ICONS.finish,
        'title' : runtrackr.RouteMap._MESSAGES.TOOLTIP.FINISH_MARKER
      }
    );

    this._map.addOverlay(startMarker);
    this._map.addOverlay(finishMarker);
  }

  // Center the map at the starting point.
  this._map.setCenter(this._route.getVertex(0));

  // TODO: How to set zoom so that most/all of current route will be shown?
  // Set the zoom level.
  this._map.setZoom(runtrackr.Map.ZOOM_DEFAULTS.ROUTE);

  // Set the modified state to false, since we have just loaded a new route.
  this._isRouteModified = false;
}

/**
 * Utility method to convert the list of markers into a list of points.
 *
 * Converts the array of GMarker objects into an array of GLatLng objects.
 * This is used mainly by the GPolyline constructor, which takes an array of
 * GLatLng points.
 *
 * @return Array<GLatLng> an array of points representing the route.
 * @private
 */
runtrackr.RouteMap.prototype._getPoints = function()
{
  var points = new Array();
  for (var i = 0; i < this._markers.length; ++i)
  {
    if (this._markers[i])
    {
      points.push(this._markers[i].getPoint());
    }
  }
  return points;
}

/**
 * Notifies the user on screen with a specified message.
 *
 * @param String message the message to notify the user with.
 * @private
 */
runtrackr.RouteMap.prototype._notify = function(message)
{
  // TODO: Use this sparingly, and only in situations where a callback cannot
  // be used.
  // TODO: Properly code to alert user in a notification area.  Consider
  // separate method or parameters for errors.
  window.alert(message);
}

