using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MapComparison
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            // get references to layers
            if (_lyrConnections == null)
            {
                _lyrConnections = _esriMap.Layers["_lyrConnections"] as ESRI.ArcGIS.Client.GraphicsLayer;
            }
            if (_lyrConnectionsC1 == null)
            {
                _lyrConnectionsC1 = _c1Map.Layers[0] as C1.Silverlight.Maps.C1VectorLayer;
            }

            // hit testing for the ESRI map
            _esriMap.MouseLeftButtonDown += (s, e) =>
                {
                    _mouseObject = HitTestMap(_esriMap, e);
                };
            _esriMap.MouseLeftButtonUp += (s, e) =>
                {
                    if (_mouseObject != null && object.Equals(_mouseObject, HitTestMap(_esriMap, e)))
                    {
                        var lm = _mouseObject as Landmark;
                        MessageBox.Show(string.Format("You should definitely visit the {0}!", lm.Name));
                        if (_chkSpending.IsChecked.Value)
                        {
                            SpatialQuery(lm);
                        }
                    }
                };

            // hit testing for the ComponentOne map
            _c1Map.MouseLeftButtonDown += (s, e) =>
                {
                    _mouseObject = HitTestMap(_c1Map, e);
                };
            _c1Map.MouseLeftButtonUp += (s, e) =>
                {
                    if (_mouseObject != null && object.Equals(_mouseObject, HitTestMap(_c1Map, e)))
                    {
                        var lm = _mouseObject as Landmark;
                        MessageBox.Show(string.Format("You should definitely visit {0}!", lm.Name));
                    }
                };

            // center objects on the map
            // (use AddHandler to get the events even if they've been handled)
            _listBox.AddHandler(
                Control.MouseLeftButtonDownEvent, 
                new MouseButtonEventHandler(_listBox_MouseLeftButtonDown), 
                true);

            // toggle connections
            _chkConnections.Click += _chkConnections_Click;

            // show/hide spending layer
            _chkSpending.Click += _chkSpending_Click;
        }

        void SpatialQuery(Landmark lm)
        {
            // url for the USA_Retail_Spending_Potential service 
            var url = "http://services.arcgisonline.com:80/ArcGIS/rest/services/Demographics/USA_Retail_Spending_Potential/MapServer/4";

            // create new query, with a list of fields to return
            // for a complete list of the fields available from this service see:
            // http://services.arcgisonline.com/ArcGIS/rest/services/Demographics/USA_Retail_Spending_Potential/MapServer/layers
            // OBJECTID (Type: esriFieldTypeOID, Alias: OBJECTID) 
            // Shape (Type: esriFieldTypeGeometry, Alias: Shape) 
            // ID (Type: esriFieldTypeString, Alias: ID, Length: 11 ) 
            // NAME (Type: esriFieldTypeString, Alias: Name, Length: 12 ) 
            // ST_ABBREV (Type: esriFieldTypeString, Alias: State Abbreviation, Length: 2 ) 
            // TOTPOP_CY (Type: esriFieldTypeInteger, Alias: 2010 Total Population) 
            // MEDHINC_CY (Type: esriFieldTypeInteger, Alias: 2010 Median Household Income) 
            // X15001_A (Type: esriFieldTypeDouble, Alias: Retail Goods: Average) 
            // X15001_I (Type: esriFieldTypeInteger, Alias: Retail Goods: Index) 
            // LANDAREA (Type: esriFieldTypeDouble, Alias: Land Area in Square Miles) 
            var query = new ESRI.ArcGIS.Client.Tasks.Query();
            query.OutFields.AddRange(new string[] { "ID", "ST_ABBREV", "TOTPOP_CY", "MEDHINC_CY", "X15001_A", "LANDAREA" });

            // specify the geometry to use for this spatial query (instead of a WHERE clause)
            var pt = GeoToMap(lm.LongLat);
            query.Geometry = new ESRI.ArcGIS.Client.Geometry.MapPoint(pt.X, pt.Y);

            // execute query
            var queryTask = new ESRI.ArcGIS.Client.Tasks.QueryTask(url);
            queryTask.ExecuteCompleted += queryTask_ExecuteCompleted;
            queryTask.Failed += queryTask_Failed;
            queryTask.ExecuteAsync(query);
        }
        void queryTask_ExecuteCompleted(object sender, ESRI.ArcGIS.Client.Tasks.QueryEventArgs e)
        {
            if (e.FeatureSet == null || e.FeatureSet.Features.Count == 0)
            {
                MessageBox.Show("No additional data is available for this landmark");
            }
            else
            {
                var sb = new System.Text.StringBuilder();
                var atts = e.FeatureSet.Features[0].Attributes;
                foreach (var kv in atts)
                {
                    sb.AppendFormat("{0}: {1}\r\n", kv.Key, kv.Value);
                }
                MessageBox.Show("Additional data for this landmark:\r\n" + sb.ToString());
            }
        }
        void queryTask_Failed(object sender, ESRI.ArcGIS.Client.Tasks.TaskFailedEventArgs e)
        {
            MessageBox.Show("Query failed: " + e.Error.Message);
        }

        // hit testing ESRI map
        object _mouseObject;
        object HitTestMap(ESRI.ArcGIS.Client.Map map, MouseButtonEventArgs e)
        {
            var pt = e.GetPosition(null);
            var list = VisualTreeHelper.FindElementsInHostCoordinates(pt, map);
            foreach (var element in list)
            {
                var fe = element as FrameworkElement;
                if (fe != null)
                {
                    var db = fe.DataContext as ESRI.ArcGIS.Client.DataBinding;
                    if (db != null)
                    {
                        return db.Attributes["DataContext"];
                    }
                }
            }
            return null;
        }
#if false // doesn't work well with clusterers (can't tell clustered from non-clustered points)
        object HitTestMap(ESRI.ArcGIS.Client.Map map, MouseButtonEventArgs e)
        {
            foreach (var layer in map.Layers)
            {
                var gl = layer as ESRI.ArcGIS.Client.GraphicsLayer;
                if (gl != null)
                {
                    var obj = HitTestLayer(gl, e);
                    if (obj != null)
                    {
                        return obj;
                    }
                }
            }
            return null;
        }
        object HitTestLayer(ESRI.ArcGIS.Client.GraphicsLayer layer, MouseButtonEventArgs e)
        {
            var pt = e.GetPosition(null);
            var list = layer.FindGraphicsInHostCoordinates(pt);
            foreach (var element in list)
            {
                return element.Attributes["DataContext"];
            }
            return null;
        }
#endif

        // hit testing ComponentOne map
        object HitTestMap(C1.Silverlight.Maps.C1Maps map, MouseButtonEventArgs e)
        {
            var pt = e.GetPosition(null);
            var list = VisualTreeHelper.FindElementsInHostCoordinates(pt, map);
            foreach (var element in list)
            {
                var fe = element as FrameworkElement;
                if (fe != null)
                {
                    if (fe.DataContext != null)
                    {
                        return fe.DataContext;
                    }
                }
            }
            return null;
        }

        // center objects on the map
        void _listBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var lm = _listBox.SelectedItem as Landmark;
            if (lm != null)
            {
                // set C1Map center
                //_c1Map.Center = lm.LongLat;
                _c1Map.Center = new Point(lm.Longitude, lm.Latitude); 

                // set ESRI map center
                _esriMap.PanTo(GeoToMap(lm.LongLat));
            }
        }

        // convert geo coordinates (lat/lon) to and from map coordinates
        // (assuming WebMercator projection).
        static ESRI.ArcGIS.Client.Projection.WebMercator _wm = new ESRI.ArcGIS.Client.Projection.WebMercator();
        ESRI.ArcGIS.Client.Geometry.MapPoint GeoToMap(Point point)
        {
            var mapPoint = new ESRI.ArcGIS.Client.Geometry.MapPoint(point.X, point.Y);
            return _wm.FromGeographic(mapPoint) as ESRI.ArcGIS.Client.Geometry.MapPoint;
        }
        Point MapToGeo(ESRI.ArcGIS.Client.Geometry.MapPoint mapPoint)
        {
            mapPoint = _wm.ToGeographic(mapPoint) as ESRI.ArcGIS.Client.Geometry.MapPoint;
            return new Point(mapPoint.X, mapPoint.Y);
        }

        // toggle retail spending potential layer
        void _chkSpending_Click(object sender, RoutedEventArgs e)
        {
            var layer = _esriMap.Layers["_retailSpendingPotential"];
            layer.Visible = _chkSpending.IsChecked.Value;
        }

        // toggle connections
        void _chkConnections_Click(object sender, RoutedEventArgs e)
        {
            // point to connect from
            var ptCenter = new Point(-79.92493611, 40.45867778);

            // remove connections from C1 map
            _lyrConnectionsC1.Children.Clear();

            // remove connections from ESRI map
            _lyrConnections.ClearGraphics();

            // add connections to both maps
            if (_chkConnections.IsChecked.Value)
            {
                // get data
                var dataModel = Resources["_dataModel"] as DataModel;

                // show connections on c1 map
                {
                    // build line
                    var points = new PointCollection();
                    points.Add(ptCenter);
                    foreach (Landmark lm in dataModel.Landmarks)
                    {
                        points.Add(lm.LongLat);
                        points.Add(points[0]);
                    }
                    var line = new C1.Silverlight.Maps.C1VectorPolyline();
                    line.Stroke = new SolidColorBrush(Colors.Orange);
                    line.StrokeThickness = 2;
                    line.Points = points;

                    // add line to layer
                    _lyrConnectionsC1.Children.Add(line);
                }

                // show connections on ESRI map
                {
                    // build line
                    var points = new ESRI.ArcGIS.Client.Geometry.PointCollection();
                    points.Add(GeoToMap(ptCenter));
                    foreach (Landmark lm in dataModel.Landmarks)
                    {
                        var pt = GeoToMap(lm.LongLat);
                        points.Add(pt);
                        points.Add(points[0]);
                    }
                    var polyline = new ESRI.ArcGIS.Client.Geometry.Polyline();
                    polyline.Paths.Add(points);

                    // add line to layer
                    ShowPolyline(polyline, new SolidColorBrush(Colors.Orange));
                }
            }
        }
        void ShowPolyline(ESRI.ArcGIS.Client.Geometry.Polyline polyline, Brush brush)
        {
            // create symbol (line)
            var symbol = new ESRI.ArcGIS.Client.Symbols.SimpleLineSymbol();
            symbol.Color = brush;
            symbol.Width = 2;
            symbol.Style = ESRI.ArcGIS.Client.Symbols.SimpleLineSymbol.LineStyle.Solid;

            // create graphic from polyline and symbol
            var graphic = new ESRI.ArcGIS.Client.Graphic();
            graphic.Symbol = symbol;
            graphic.Geometry = polyline;

            // add graphic to connection layer
            _lyrConnections.Graphics.Add(graphic);
        }

        void _btnMovePoint_Click(object sender, RoutedEventArgs e)
        {
            var dataModel = Resources["_dataModel"] as DataModel;
            var edt = dataModel.Landmarks as IEditableCollectionView;
            foreach (Landmark lm in dataModel.Landmarks)
            {
                if (lm.Name.StartsWith("Statue"))
                {
                    for (int i = 0; i < 10; i++)
                    {
                        edt.EditItem(lm);
                        lm.Latitude += 0.5;
                        lm.Longitude += 0.5;
                        edt.CommitEdit();
                    }
                    break;
                }
            }
        }
    }
}
