using System;
using System.Collections.Generic;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.MapKit;  // required
using MonoTouch.CoreLocation;  // required
 
namespace MapKit01
{
    /// <summary>
    /// MapKit103 
    /// http://conceptdev.blogspot.com/2009/09/monotouch-mapkit-103.html
    /// </summary>
    public class Application
    {
        static void Main (string[] args)
        {
            try {  // try-catch for debugging only
                UIApplication.Main (args);
            } catch (Exception ex)
            {
                Console.WriteLine("Main " + ex.Message);
            }
        }
    }
 
    /// <summary>
    /// The name AppDelegate is referenced in the MainWindow.xib file.
    /// It is a partial class that has a Register() attribute for MonoTouch
    /// </summary>
    public partial class AppDelegate : UIApplicationDelegate
    {
        MKReverseGeocoder geoCoder;
        public CLLocation mylocation;
        CLLocationManager locationManager;
        
        // This method is invoked when the application has loaded its UI and its ready to run
        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            mapType.ValueChanged += delegate {
                if (mapType.SelectedSegment == 0)
                        mapView.MapType = MonoTouch.MapKit.MKMapType.Standard;
                else if (mapType.SelectedSegment == 1)
                        mapView.MapType = MonoTouch.MapKit.MKMapType.Satellite;
                else if (mapType.SelectedSegment == 2)
                        mapView.MapType = MonoTouch.MapKit.MKMapType.Hybrid;
            };
            
            textfieldLatitude.AllEditingEvents += delegate {
                Console.WriteLine("alleditingevents");
            };
            textfieldLatitude.Ended += delegate { // move cursor to Longitude field
                textfieldLongitude.BecomeFirstResponder();
            };
            textfieldLongitude.AllEditingEvents += delegate {
                Console.WriteLine("alleditingevents");
            };
            textfieldLongitude.Ended += delegate {  // hide keyboard
                textfieldLongitude.ResignFirstResponder();
            };
            buttonCurrent.TouchDown += delegate {  // reverse geocode CurrentLocation (from GPS)
                Console.WriteLine("geoloc location set from LocationManager " + locationManager.Location.Coordinate.Latitude
                                                                              +","+locationManager.Location.Coordinate.Longitude);
                CLLocationCoordinate2D location2 = new CLLocationCoordinate2D(locationManager.Location.Coordinate.Latitude
                                                                              ,locationManager.Location.Coordinate.Longitude);
                geoCoder = new MKReverseGeocoder(location2);
                geoCoder.Delegate = new GeoCoderDelegate(this);
                geoCoder.Start();
            };
            buttonMap.TouchDown += delegate { // reverse geocode Map center point
                Console.WriteLine("geoloc location set from MapView CenterCoordinate");
                geoCoder = new MKReverseGeocoder(mapView.CenterCoordinate);
                geoCoder.Delegate = new GeoCoderDelegate(this);
                geoCoder.Start();
            };
            buttonGeoloc.TouchDown += delegate { // reverse geocode Lat,Long typed by user
                try
                {
                    Console.WriteLine("geoloc location set in button " + textfieldLatitude.Text +","+ textfieldLongitude.Text);
                    CLLocationCoordinate2D location1 = new CLLocationCoordinate2D(
                                             Convert.ToDouble(textfieldLatitude.Text),
                                             Convert.ToDouble(textfieldLongitude.Text));
                    geoCoder = new MKReverseGeocoder(location1);
                    geoCoder.Delegate = new GeoCoderDelegate(this);
                    geoCoder.Start();
                } 
                catch (Exception e)
                {
                    Console.WriteLine("buttonGeoloc.TouchDown EXCEPTION:" + e.Message);
                    // Probably invalid keyboard input, just reset for now...
                    textfieldLatitude.Text = "-33.867139";
                    textfieldLongitude.Text = "151.207114";
                }
            };
            buttonGo.TouchDown += delegate { // move map to Lat,Long typed by user
                mapView.SetCenterCoordinate(new CLLocationCoordinate2D(
                                             Convert.ToDouble(textfieldLatitude.Text),
                                             Convert.ToDouble(textfieldLongitude.Text)),true);     
            };
            buttonGoCurrent.TouchDown += delegate { // move map to Current Location (from GPS)
                CLLocationCoordinate2D location3 = new CLLocationCoordinate2D(locationManager.Location.Coordinate.Latitude
                                                                              ,locationManager.Location.Coordinate.Longitude);
                mapView.SetCenterCoordinate(location3,true);     
            };
            
            mapView.ShowsUserLocation = true;
            mapView.MapType = MonoTouch.MapKit.MKMapType.Standard;
            mapView.Delegate = new MapViewDelegate(this);  // RegionChanged, GetViewForAnnotation 
            
            locationManager = new CLLocationManager();
            locationManager.Delegate = new LocationManagerDelegate(mapView, this);
            locationManager.StartUpdatingLocation();
            
            // Perform the first ReverseGeocode as the app starts up! No reason, just because we can...
            CLLocationCoordinate2D location = new CLLocationCoordinate2D(0,0);
            location = new CLLocationCoordinate2D(-33.867139,151.207114);    
            Console.WriteLine("geoloc location set");
            geoCoder = new MKReverseGeocoder(location);
            geoCoder.Delegate = new GeoCoderDelegate(this);
            geoCoder.Start();
            
            MyAnnotation a = new MyAnnotation(
                                  new CLLocationCoordinate2D(-33.867139,151.207114)
                                , "Home"
                                , "is where the heart is"
                              );
            Console.WriteLine("This adds a custom placemark in Sydney, Australia :)");
            mapView.AddAnnotationObject(a); 
        
            window.MakeKeyAndVisible ();
            return true;
        }
 
        // This method is required in iPhoneOS 3.0
        public override void OnActivated (UIApplication application)
        {
        }
        
        /// <summary>
        /// MonoTouch definition seemed to work without too much trouble
        /// </summary>
        private class LocationManagerDelegate: CLLocationManagerDelegate
        {
            private MKMapView _mapview;
            private AppDelegate _appd;
            private int _count = 0;
            public LocationManagerDelegate(MKMapView mapview, AppDelegate appd):base()
            {
                _mapview = mapview;
                _appd=appd;
                Console.WriteLine("Delegate created");
            }
            /// <summary>
            /// Whenever the GPS sends a new location, update text in label
            /// and increment the 'count' of updates AND reset the map to that location 
            /// </summary>
            public override void UpdatedLocation (CLLocationManager manager, CLLocation newLocation, CLLocation oldLocation)
            {
                MKCoordinateSpan span = new MKCoordinateSpan(0.2,0.2);
                MKCoordinateRegion region = new MKCoordinateRegion(newLocation.Coordinate,span);
                _appd.mylocation = newLocation;
                _mapview.SetRegion(region, true);
                _appd.labelInfo.Text = "UserLocation (" + (_count++) + ") " + newLocation.Coordinate.Latitude + ", " + newLocation.Coordinate.Longitude;
                Console.WriteLine("Location updated");
            }
            public override void Failed (CLLocationManager manager, NSError error)
            {
                _appd.labelInfo.Text = "Failed to find location";
                Console.WriteLine("Failed to find location");
                base.Failed (manager, error);
            }
        }
        
        /// <summary>
        /// MKAnnotation is an abstract class (in Objective C I think it's a protocol).
        /// Therefore we must create our own implementation of it. Since all the properties
        /// are read-only, we have to pass them in via a constructor.
        /// </summary>
        public class MyAnnotation : MKAnnotation
        {
            private CLLocationCoordinate2D _coordinate;
            private string _title, _subtitle;
            public override CLLocationCoordinate2D Coordinate {
                get {
                    return _coordinate;
                }
            }
            public override string Title {
                get {
                    return _title;
                }
            }
            public override string Subtitle {
                get {
                    return _subtitle;
                }
            }
            /// <summary>
            /// custom constructor
            /// </summary>
            public MyAnnotation (CLLocationCoordinate2D coord, string t, string s) : base()
            {
                _coordinate=coord;
                 _title=t; 
                _subtitle=s;
            }
        }
 
        /// <summary>
        /// 'handler' for messages from ReverseGeocoderDelegate; pass in 
        /// reference to main window via ctor so we can chat with the UI
        /// </summary>
        public class GeoCoderDelegate : MKReverseGeocoderDelegate
        {
            AppDelegate _appd;
            public GeoCoderDelegate (AppDelegate appd)
            {
                _appd = appd;    
            }
 
            /// <summary>
            /// When the reverse geocode finds a location, it calls this method
            /// which puts the placemark on the map as an Annotation
            /// </summary>
            public override void FoundWithPlacemark (MKReverseGeocoder geocoder, MKPlacemark placemark)
            {
                Console.WriteLine("Found placemark in " + placemark.Country);
                Console.WriteLine(placemark.SubThoroughfare                                  +placemark.Thoroughfare                                  +placemark.SubLocality                                  +placemark.Locality                                  +placemark.SubAdministrativeArea              +placemark.AdministrativeArea                                  + placemark.Country);
                _appd.labelPlacemark.Text = placemark.SubThoroughfare+" "+placemark.Thoroughfare
                    +" " + placemark.Locality + " "+placemark.AdministrativeArea + " " + placemark.Country;
                
                try
                {
                    _appd.mapView.AddAnnotationObject(placemark); 
                }
                catch (Exception ex)
                {
                    Console.WriteLine("FoundWithPlacemark" + ex.Message);
                }
            }
            /// <summary>
            /// Exposed by MonoTouch, just override to make it work
            /// </summary>
            public override void FailedWithError (MKReverseGeocoder gc, NSError error)
            {
                _appd.labelPlacemark.Text = "Reverse Geocoder " + error.LocalizedDescription;
                Console.WriteLine("Reverse Geocoder failed");
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        public class MapViewDelegate : MKMapViewDelegate
        {
            private AppDelegate _appd;
            public MapViewDelegate (AppDelegate appd):base()
            {
                _appd = appd;
            }
            /// <summary>
            /// When user moves the map, update lat,long text in label
            /// </summary>
            public override void RegionChanged (MKMapView mapView, bool animated)
            {
                Console.WriteLine("Region did change");
                _appd.labelCurrent.Text = "Map Center " + mapView.CenterCoordinate.Latitude + ", " + mapView.CenterCoordinate.Longitude;
            }
            /// <summary>
            /// Seems to work in the Simulator now
            /// </summary>
            public override MKAnnotationView GetViewForAnnotation (MKMapView mapView, NSObject annotation)
            {
                Console.WriteLine("attempt to get view for MKAnnotation "+annotation);
                try
                {
                    var anv = mapView.DequeueReusableAnnotation("thislocation");
                    if (anv == null)
                    {
                        Console.WriteLine("creating new MKAnnotationView");
                        var pinanv = new MKPinAnnotationView(annotation, "thislocation");
                        pinanv.AnimatesDrop = true;
                        pinanv.PinColor = MKPinAnnotationColor.Green;
                        pinanv.CanShowCallout = true;
                        anv = pinanv;
                    }
                    else 
                    {
                        anv.Annotation = annotation;
                    }
                    return anv;
                } catch (Exception ex)
                {
                    Console.WriteLine("GetViewForAnnotation Exception " + ex);
                    return null;
                }
            }
        }
    }
}