using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Maps.MapControl;
using SLSearchMap.Translators;
using SLSearchMap.RouteService;
using System.Collections.ObjectModel;

namespace SLSearchMap
{
    public partial class MainPage : UserControl
    {
        #region Private fields

        // Ui logic
        Location clickedLocation = new Location();
        int countLocations = 0; // use an integer in case we want to implement multiple point

        // -- WS
        // Routing service client
        RouteService.RouteServiceClient client = new RouteService.RouteServiceClient();
        // GeoData service client
        GeoDataService.GeoDataPointServiceClient dataClient = new GeoDataService.GeoDataPointServiceClient();

        // Route Locations
        MapPolyline routeLine = new MapPolyline();
        List<Location> routePoints = new List<Location>();

        #endregion

        public MainPage()
        {
            InitializeComponent();

            // Map events
            this.map.MouseClick += new EventHandler<Microsoft.Maps.MapControl.MapMouseEventArgs>(map_MouseClick);
        }

        #region Map Events

        void map_MouseClick(object sender, Microsoft.Maps.MapControl.MapMouseEventArgs e)
        {
            //clickedLocation = 
            Location locTemp;
            if (this.map.TryViewportPointToLocation(e.ViewportPoint, out locTemp))
            {

                if (countLocations == 0)
                {
                    clickedLocation = locTemp;

                    countLocations = 1;
                }
                else
                {
                    // Calculate the route between the two clicked locations
                    client.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(client_CalculateRouteCompleted);

                    RouteRequest request = new RouteRequest();
                    request.Culture = this.map.Culture;
                    request.Waypoints = new ObservableCollection<Waypoint>();

                    // Add points
                    request.Waypoints.Add(new Waypoint() { Location = clickedLocation });
                    request.Waypoints.Add(new Waypoint() { Location = locTemp });

                    // Don't raise exceptions.
                    request.ExecutionOptions = new ExecutionOptions();
                    request.ExecutionOptions.SuppressFaults = true;

                    // Only accept results with high confidence.
                    request.Options = new RouteOptions();
                    request.Options.RoutePathType = RoutePathType.Points;

                    this.map.CredentialsProvider.GetCredentials(
                        (Credentials credentials) =>
                        {
                            //Pass in credentials for web services call.
                            //Replace with your own Credentials.
                            request.Credentials = credentials;

                            // Make asynchronous call to fetch the data ... pass state object.
                            this.client.CalculateRouteAsync(request);
                        });

                    // Update for the next element
                    countLocations = 0;
                }
            }
        }

        #endregion

        #region Async routing methods

        void client_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
        {
            // Clear the control
            this.LayerRoute.Children.Clear();
            this.LayerCustomData.Children.Clear();
            
            // Add the route to the map control
            this.addRoute(e);


            // Request to search points near the route
            dataClient.GetNearPointsCompleted += 
                new EventHandler<GeoDataService.GetNearPointsCompletedEventArgs>(dataClient_GetNearPointsCompleted);
            
            if (this.routeLine.Locations.Any())
                if(this.routeLine.Locations.Count < 50)
                    dataClient.GetNearPointsAsync(
                        this.routeLine.Locations.Select(p => GeoPointTranslator.ToGeoPointCoord(p)).ToList(), 
                        5000
                    );
                else
                    dataClient.GetNearPointsAsync(
                        this.routePoints.Select(p => GeoPointTranslator.ToGeoPointCoord(p)).ToList(), 
                        5000
                    );


        }

        #endregion

        #region Async data methods

        void dataClient_GetNearPointsCompleted(object sender, GeoDataService.GetNearPointsCompletedEventArgs e)
        {
            if (e.Result != null)
            {
                foreach (var curPoint in e.Result)
                {
                    Pushpin curPin = new Pushpin()
                    {
                        Location = new Location()
                        {
                            Latitude = curPoint.Latitude,
                            Longitude = curPoint.Longitude
                        },
                        Tag = curPoint.Title + " " + curPoint.Description
                    };

                    this.LayerCustomData.Children.Add(curPin);
                }
            }
        }

        #endregion

        #region Private Map Manipulation methods

        private void addRoute(RouteService.CalculateRouteCompletedEventArgs e)
        {
            Color routeColor = Colors.Blue;
            SolidColorBrush routeBrush = new SolidColorBrush(routeColor);

            routeLine = new MapPolyline();
            routeLine.Locations = new LocationCollection();
            routeLine.Stroke = routeBrush;
            routeLine.Opacity = 0.75;
            routeLine.StrokeThickness = 5.0;
            foreach (Location p in e.Result.Result.RoutePath.Points)
                routeLine.Locations.Add(new Location(p.Latitude, p.Longitude));
            LayerRoute.Children.Add(routeLine);

            LocationRect rect = new LocationRect(routeLine.Locations[0], routeLine.Locations[routeLine.Locations.Count - 1]);
            this.routePoints = new List<Location>();
            foreach (RouteService.ItineraryItem itineraryItem in e.Result.Result.Legs[0].Itinerary)
            {
                // Set pin style
                Ellipse point = new Ellipse();
                point.Width = 10;
                point.Height = 10;
                point.Fill = new SolidColorBrush(Colors.Red);
                point.Opacity = 0.65;

                // Get geo info
                Location location = new Location(itineraryItem.Location.Latitude, itineraryItem.Location.Longitude);
                this.routePoints.Add(location);
                // Add the pin to the map control
                MapLayer.SetPosition(point, location);
                MapLayer.SetPositionOrigin(point, PositionOrigin.Center);

                // Pin other info
                point.Tag = itineraryItem;
                //point.MouseEnter += new System.Windows.Input.MouseEventHandler(point_MouseEnter);
                //point.MouseLeave += new System.Windows.Input.MouseEventHandler(point_MouseLeave);

                this.LayerRoute.Children.Add(point);
            }

            this.map.SetView(rect);
        }

        #endregion
    }
}
