context: /*
   * 	MuddyTummy Core
   *
   * Copyright (c) 2010-2012 MuddyTummy Software, LLC
   * 
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   * 
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * 
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  /* System */
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Linq;
  using System.Net;
  using System.IO;
  using System.Runtime.Remoting.Messaging;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading.Tasks;
  using System.Xml;
  using System.Xml.XPath;
  
  namespace MuddyTummy.Cloud
  {
      public sealed class GoogleAPIs
      {
          private static string ApiSensorArg { get { return "false"; } } /* TODO: QUERY VALUE FROM DEVICE: not using a sensor to determine the location */
  
          /*
           * Constants.
           */
          private static readonly Uri _uriDynamicMapLocal = new Uri(@"maps:", UriKind.Absolute);
          private static readonly Uri _uriDynamicMap = new Uri(@"https://maps.google.com/maps/", UriKind.Absolute);
  
          private static readonly Uri _uriMapsApis = new Uri(@"https://maps.googleapis.com/maps/api/");
          private static readonly Uri _uriStaticMapApi = new Uri(_uriMapsApis, @"staticmap");
          private static readonly Uri _uriPlaceDetailsApi = new Uri(_uriMapsApis, @"place/details/xml");
          private static readonly Uri _uriPlacesApi = new Uri(_uriMapsApis, @"place/search/xml");
          private static readonly Uri _uriGeocodingApi = new Uri(_uriMapsApis, @"geocode/xml");
  
          private const string _strYourGoogleAPIKey = @"<your Google API key goes here>";
          private const string _strYourAdMobPublisherID = @"<your AdMob Publisher ID goes here>";
          
          private const uint cmsecsWebTimeout = 30 * 1000; /* 30 seconds */
  
          public static string GoogleAPIKey { get { return _strYourGoogleAPIKey; } }
          public static string AdMobPublisherID { get { return _strYourAdMobPublisherID; } }
          
          /*
           * Enums.
           */
          public enum PlacesType {None = 0, Food, Drink, Parking};
              
          /*
           * 2D location expressed as a latitude and a longitude.
           */
          public class Location2D : ICloneable
          {
              /*
               * Member variables.
               */
              private double _latitude, _longitude;
              
              /*
               * Construction/destruction.
               */
              public Location2D()
              {
                  _latitude = double.MinValue;
                  _longitude = double.MinValue;
              }
              public Location2D(double latitude, double longitude)
              {
                  _latitude = latitude;
                  _longitude = longitude;
              }
              
              /*
               * Properties.
               */
              public static Location2D Empty {get {return new Location2D();}}
              
              public bool IsValid {get {return HasLatitude && HasLongitude;}}
              
              public bool HasLatitude {get {return double.MinValue != this.Latitude;}}
              public bool HasLongitude {get {return double.MinValue != this.Longitude;}}
          
              public double Latitude {get {return _latitude;}}
              public double Longitude {get {return _longitude;}}
              
              /*
               * Implement ICloneable.
               */
              public object Clone() {return this.MemberwiseClone();}
              
              /*
               * Implement Equal().
               */
              public override int GetHashCode ()
              {
                  return _latitude.GetHashCode() ^ _longitude.GetHashCode();
              } 
              public override bool Equals(object objOther)
              {
                  if (null == objOther || this.GetType() != objOther.GetType())
                      return false;
                  
                  return (objOther as Location2D).Latitude == this.Latitude && (objOther as Location2D).Longitude == this.Longitude;
              }
          }		
          
          /*
           * Place.
           */
          public class Place
          {
              public struct IdRef
              {
                  public string Id {get; set;}
                  public string Reference {get; set;}
              }
              
              /*
               * Member variables.
               */
              private IdRef _idref;
              private List<string> _types = new List<string>();
              
              /*
               * Construction/destruction.
               */
              public Place()
              {
                  this.Id = this.Reference = string.Empty;
              }
              
              /*
               * Properties.
               */
              public Location2D Location2D {get; set;}
              
              public string Id {get {return _idref.Id;} set {_idref.Id = value;}}
              public string Reference {get {return _idref.Reference;} set {_idref.Reference = value;}}
              public IdRef IdRefTuple {get {return _idref;}}
              
              public List<string> Types {get {return _types;}}
              
              public string Name {get; set;}
              public string Vicinity {get; set;}
              public Uri IconUri {get; set;}
              
              public PlaceDistanceData DistanceFrom {get; set;}
              
              /*
               * Methods.
               */
              public void CalcPlaceDistanceFrom(Location2D location2dFrom)
              {
                  double metres = MuddyTummy.GeoHelpers.DeltaMetresDistance(location2dFrom.Latitude, location2dFrom.Longitude,
                                                                            this.Location2D.Latitude, this.Location2D.Longitude);
                  this.DistanceFrom = new PlaceDistanceData(metres, location2dFrom);
              }
          }
          
          /*
           * Place details.
           */
          public class PlaceDetails : Place
          {
              /*
               * Member variables.
               */
              public Address _address = new Address();
              
              /*
               * Construction/destruction.
               */
              public PlaceDetails()
              {
                  this.Phone = this.IntlPhone = string.Empty;
                  this.Rating = -1;
              }
  
              /*
               * Canonicalize a phone string.
               */
              private static string CanonicalizePhoneString(string strPhone)
              {
                  if (string.IsNullOrWhiteSpace(strPhone))
                      return string.Empty;
  
                  /*
                   * Take just our digits (whilst mapping to keypad).
                   */
                  string strPhoneDigits = string.Empty;
                  foreach (char ch in strPhone)
                  {
                      char chLocal = ch;
  
                      if (char.IsUpper(chLocal))
                      {
                          int offset = chLocal - 'A';
                          if ('A' - 'A' <= offset && offset <= 'C' - 'A')
                              chLocal = '2';
                          else if ('D' - 'A' <= offset && offset <= 'F' - 'A')
                              chLocal = '3';
                          else if ('G' - 'A' <= offset && offset <= 'I' - 'A')
                              chLocal = '4';
                          else if ('J' - 'A' <= offset && offset <= 'L' - 'A')
                              chLocal = '5';
                          else if ('M' - 'A' <= offset && offset <= 'O' - 'A')
                              chLocal = '6';
                          else if ('P' - 'A' <= offset && offset <= 'S' - 'A')
                              chLocal = '7';
  
gt:                         else if ('T' - 'A' <= offset && offset <= 'V' - 'A')