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
{
    public class Application
    {
        static void Main (string[] args)
        {
            UIApplication.Main (args);
        }
    }
 
    // The name AppDelegate is referenced in the MainWindow.xib file.
    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);     
            };
            Console.WriteLine("Setup mapView; don't use ShowsUserLocation as it too easy (but it works)");
            //mapView.ShowsUserLocation = true;
            mapView.MapType = MonoTouch.MapKit.MKMapType.Standard;
            mapView.Delegate = new MapViewDelegate(this);  // RegionChanged works, GetViewForAnnotation DOESN'T 
            
            locationManager = new CLLocationManager();
            locationManager.Delegate = new LocationManagerDelegate(mapView, this);
            locationManager.StartUpdatingLocation();
            //locationManager.DesiredAccuracy = ??? // need the CONST values for this setting
            
            
            // 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();
            
            // Annotation DOES NOT WORK at the moment...
            MyAnnotation a = new MyAnnotation(
                                              new CLLocationCoordinate2D(40.8148495,-73.6227325)
                                            , "Home"
                                            , "is where the heart is"
                                          );
            // This BREAKS - see the MapViewDelegate implementation below...
            //mapView.AddAnnotation(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)
            {
                _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)
            {
                //base.UpdatedLocation (manager, newLocation, 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>
        /// Annotations are NOT working at present, so this is 
        /// here for testing. I have tried adding ExportAttribute
        /// to title/subtitle _methods_ rather than overriding
        /// the c# properties in the MonoTouch.MapKit.MKAnnotation
        /// implementation - doesn't seem to help though...
        /// </summary>
        [Model]
        [Register("MKAnnotation")]
        public class MyAnnotation : MKAnnotation
        {
            private CLLocationCoordinate2D _coordinate;
            private string _title, _subtitle;
            [Export("coordinate")]
            public override CLLocationCoordinate2D coordinate{get{ return _coordinate;}}
            [Export("title")]
            public string title () {return _title;}
            //public override string Title {get {return _title;}}
            [Export("subtitle")]
            public string subtitle () {return _subtitle;}
            //public override string Subtitle {get {return _subtitle;}}
            
            public MyAnnotation (CLLocationCoordinate2D coord, string t, string s)
            {
                _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>
        [Register]
        public class GeoCoderDelegate : MKReverseGeocoderDelegate
        {
            AppDelegate _appd;
            public GeoCoderDelegate (AppDelegate appd)
            {
                _appd = appd;    
            }
            /// <summary>
            /// Not currently exposed by MonoTouch, ExportAttribute seems to work though
            /// </summary>
            [Export("reverseGeocoder:didFindPlacemark:")]
            public void FoundPlacemark(MKReverseGeocoder geocoder, MKPlacemark placemark)
            {
                Console.WriteLine("Found placemark in " + placemark.Country);
                //Console.WriteLine(placemark.Country);
                //Console.WriteLine(placemark.AdministrativeArea);
                //Console.WriteLine(placemark.SubAdministrativeArea);
                //Console.WriteLine(placemark.Locality);
                //Console.WriteLine(placemark.SubLocality);
                //Console.WriteLine(placemark.PostalCode);
                //Console.WriteLine(placemark.Thoroughfare);
                //Console.WriteLine(placemark.SubThoroughfare);
                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;
                
                //_appd.mapView.AddAnnotation(placemark); // Can't do this, even though in ObjC you 'can' due to MKPlacemark implementing MKAnnotation @protocol
            }
            /// <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>
        /// Had lots of problems with this class. RegionChanged did NOT work
        /// without an ExportAttribute (caused stack dump).
        /// </summary>
        [Register]
        public class MapViewDelegate : MKMapViewDelegate
        {private AppDelegate _appd;
            public MapViewDelegate (AppDelegate appd)
            {
                _appd = appd;
            }
            /// <summary>
            /// When user moves the map, update lat,long text in label
            /// </summary>
            [Export("mapView:RegionDidChangeAnimated:")]
            public override void RegionChanged (MKMapView mapView, bool animated)
            {
                Console.WriteLine("Region did change");
                //base.RegionChanged (mapView, animated);
                _appd.labelCurrent.Text = "Map Center " + mapView.CenterCoordinate.Latitude + ", " + mapView.CenterCoordinate.Longitude;
            }
 
            /// <summary>
            /// No matter what I try with this method, it breaks...
            /// </summary>
            /// <remarks>
            /// Got a SIGSEGV while executing native code. This usually indicates
            /// a fatal error in the mono runtime or one of the native libraries 
            /// used by your application.
            /// 
            /// warning: Trying to remove a section from the ordered section list that did not exist at 0x2d2000.
            /// warning: Could not find object file "/var/folders/-L/-LDuG6p1HfKIbOkyX-sSd++++TI/-Tmp-/tmp58110387.tmp/main.o" - no debug information available for "/var/folders/-L/-LDuG6p1HfKIbOkyX-sSd++++TI/-Tmp-/tmp58110387.tmp/main.m".
            /// </remarks>
            [Export("mapView:viewForAnnotation:")]
            public override MKAnnotationView GetViewForAnnotation(MKMapView mapView, MKAnnotation annotation)
            {
                Console.WriteLine("attempt to get view for MKAnnotation " + annotation.coordinate.Latitude +", "+annotation.coordinate.Longitude);
                var anv = mapView.DequeueReusableAnnotation("thislocation");
                if (anv == null)
                {
                    Console.WriteLine("creating new MKAnnotationView");
                    var pinanv = new MKPinAnnotationView();
                    //pinanv.AnimatesDrop = true;
                    //pinanv.PinColor = MKPinAnnotationColor.Green;
                    anv = pinanv;
                }
                else {    anv.Annotation = annotation;
                }
                return anv;
                
                //MKPinAnnotationView annView = new MKPinAnnotationView();
                //annView.Annotation = annotation;
                //annView.ReuseIdentifier = "currentloc";
                //annView.AnimatesDrop = true;
                //return annView;
            }
            
        }
    }
}