using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Maps.MapControl.Core;
using System.Drawing;
using Microsoft.Maps.MapControl;

namespace WebSamples.Utilities.Geo
{
    public class SpatialTools
    {
        #region SQLGeomtryType To ShapeType Conversion

        public static Constants.ShapeType? SQLGeomtryTypeToShapeType(System.Data.SqlTypes.SqlString sqlString)
        {
            switch (sqlString.ToString())
            {
                case "Point":
                    return Constants.ShapeType.Point;
                case "MultiPoint":
                    return Constants.ShapeType.Multipoint;
                case "LineString":
                    return Constants.ShapeType.Polyline;
                case "MultiLineString":
                    return Constants.ShapeType.Multiline;
                case "Polygon":
                    return Constants.ShapeType.Polygon;
                case "MultiPolygon":
                    return Constants.ShapeType.Multipolygon;
            }

            return null;
        }

        #endregion

        #region Coordinate/PointF conversions

        public static PointF CoordinateToPointF(Location latlong, int zoomRatio, int zoomLevel)
        {
            //Formulas based on following article:
            //http://msdn.microsoft.com/en-us/library/bb259689.aspx

            double mapResolution = 156543.04 * Math.Cos(latlong.Latitude * Math.PI / 180) / Math.Pow(2, (double)zoomLevel);

            //calculate pixel coordinate of location
            double tileZoomRatio = (double)256 / (double)zoomRatio;
            double sinLatitude = Math.Sin(MercatorUtility.DegreesToRadians(latlong.Latitude));

            //If Latitude == 90 or -90 then Y will become infinity when Math.Log is calculated
            if (sinLatitude == 1 || sinLatitude == -1)
            {
                sinLatitude += 0.0000000000001;
            }
            GeoConversionHelper converter = new GeoConversionHelper();
            double latLength = converter.GetLatLengthFromLatitude(latlong.Latitude);
            double lngLength = converter.GetLngLengthFromLatitude(latlong.Latitude);

            //double x = ((latlong.Longitude + 180) / 360) * mapResolution;
            double x = ((256 / 2) - (latLength / mapResolution));
            double y = ((256 / 2) + (lngLength / mapResolution));
            //double y = (0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) * Constants.ForumlaConstants.OneBy4PI) * mapResolution;

            return new PointF((float)x, (float)y);
        }

        //public static PointF CoordinateToRelativePointF(Location latlong, PointF topLeftPixel, int zoom)
        //{
        //    PointF loactionPixel = CoordinateToPointF(latlong, zoom, zoom);

        //    //calculate relative point cooridnates of location
        //    float x = loactionPixel.X - topLeftPixel.X;
        //    float y = loactionPixel.Y - topLeftPixel.Y;

        //    return new PointF(x, y);
        //}

        //public static PointF[] CoordinateArrayToRelativePointFArray(LocationCollection latlongs, PointF topLeftPixel, double zoomMapWidth)
        //{
        //    PointF[] points = new PointF[latlongs.Count];

        //    for (int i = 0; i < latlongs.Count; i++)
        //    {
        //        double sinLatitude = Math.Sin(MercatorUtility.DegreesToRadians(latlongs[i].Latitude));

        //        //If Latitude == 90 or -90 then Y will become infinity when Math.Log is calculated
        //        if (sinLatitude == 1 || sinLatitude == -1)
        //        {
        //            sinLatitude += 0.0000000000001;
        //        }

        //        double x = ((latlongs[i].Longitude + 180) / 360) * zoomMapWidth - topLeftPixel.X;
        //        double y = (0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) * Constants.ForumlaConstants.OneBy4PI) * zoomMapWidth - topLeftPixel.Y;

        //        points[i] = new PointF((float)x, (float)y);
        //    }

        //    return points;
        //}

        public static double ZoomMapWidth(int zoom)
        {
            return 256 * Math.Pow(2, zoom);
        }

        #endregion

        #region QuadKey to LocationRect conversions

        public static LocationRect QuadKeyToLocationRect(QuadKey quadkey)
        {
            System.Drawing.Point TopLeftPixel = new System.Drawing.Point();
            TopLeftPixel.X = quadkey.X * 256;
            TopLeftPixel.Y = quadkey.Y * 256;

            System.Drawing.Point BottomRightPixel = new System.Drawing.Point();
            BottomRightPixel.X = TopLeftPixel.X + 256;
            BottomRightPixel.Y = TopLeftPixel.Y + 256;

            return new LocationRect(PointToLocationAtZoom(TopLeftPixel, quadkey.ZoomLevel), PointToLocationAtZoom(BottomRightPixel, quadkey.ZoomLevel));
        }

        #endregion

        #region Pixel Point to Location Conversion

        /// <summary>
        /// Converts a pixel point into a Location object
        /// Pixel point is relative to top left hand corner of the map at zoom level (Quadkey math)
        /// </summary>
        /// <param name="pixel">Pixel point coordinate</param>
        /// <param name="zoom">Zoom Level</param>
        /// <returns></returns>
        public static Location PointToLocationAtZoom(System.Drawing.Point pixel, int zoom)
        {
            double Longitude = (((double)pixel.X * 360) / (256 * Math.Pow(2, zoom))) - 180;
            double efactor = Math.Exp((0.5 - (double)pixel.Y / 256 / Math.Pow(2, zoom)) * 4 * Math.PI);
            double Latitude = (Math.Asin((efactor - 1) / (efactor + 1))) * (180 / Math.PI);
            return new Location(Latitude, Longitude);
        }

        #endregion
    }
}
