using System;
using System.Threading;
using System.Threading.Tasks;
using Billing.Models;
using Billing.UI;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Map = Xamarin.Forms.Maps.Map;

namespace Billing.Views
{
    public class ViewLocationPage : BillingPage
    {
        public event EventHandler<Location> Synced;

        private readonly Bill bill;

        private CancellationTokenSource tokenSource;

        public ViewLocationPage(Bill bill)
        {
            On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(false);
            this.bill = bill;
            Title = bill.Name;

            ToolbarItems.Add(new ToolbarItem
            {
                IconImageSource = "location.png",
                Order = ToolbarItemOrder.Primary,
                Command = new Command(OnSynced)
            });

            if (bill.Latitude != null && bill.Longitude != null)
            {
                var (longitude, latitude) = (bill.Longitude.Value, bill.Latitude.Value).Wgs84ToGcj02();
                var position = new Position(latitude, longitude);
                var mapSpan = new MapSpan(position, 0.01, 0.01);
                Content = new Map(mapSpan)
                {
                    Pins =
                    {
                        new Pin
                        {
                            Label = bill.Name,
                            Type = PinType.Generic,
                            Position = position,
                            Address = bill.Store
                        }
                    }
                };
            }
        }

        protected override void OnDisappearing()
        {
            if (tokenSource != null && !tokenSource.IsCancellationRequested)
            {
                tokenSource.Cancel();
            }
            base.OnDisappearing();
        }

        private async void OnSynced()
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                if (tokenSource != null)
                {
                    return;
                }
                var location = await GetCurrentLocation();
                if (location != null)
                {
                    Synced?.Invoke(this, location);

                    MainThread.BeginInvokeOnMainThread(() =>
                    {
                        var (longitude, latitude) = (location.Longitude, location.Latitude).Wgs84ToGcj02();
                        var position = new Position(latitude, longitude);
                        var mapSpan = new MapSpan(position, 0.01, 0.01);
                        Content = new Map(mapSpan)
                        {
                            Pins =
                            {
                                new Pin
                                {
                                    Label = bill.Name,
                                    Type = PinType.Generic,
                                    Position = position,
                                    Address = bill.Store
                                }
                            }
                        };
                    });
                }
            }
        }

        private async Task<Location> GetCurrentLocation()
        {
            try
            {
                var request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(10));
                tokenSource = new CancellationTokenSource();
                var status = await Helper.CheckAndRequestPermissionAsync<Permissions.LocationWhenInUse>();
                if (status != PermissionStatus.Granted)
                {
                    return null;
                }
                return await Geolocation.GetLocationAsync(request, tokenSource.Token);
            }
            catch (FeatureNotSupportedException) { }
            catch (FeatureNotEnabledException) { }
            catch (PermissionException) { }
            catch (Exception ex)
            {
                Helper.Error("location.get", ex);
            }
            tokenSource = null;
            return null;
        }
    }
}