using System;
using System.Linq;
using System.Threading.Tasks;
using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.UI.Theme;
using Pixiview.Utils;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Pixiview.Illust
{
    [QueryProperty("IllustId", "id")]
    public partial class ViewIllustPage : AdaptedPage
    {
        public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(
            nameof(IsFavorite), typeof(ImageSource), typeof(ViewIllustPage));
        public static readonly BindableProperty IllustsProperty = BindableProperty.Create(
            nameof(Illusts), typeof(IllustDetailItem[]), typeof(ViewIllustPage));
        public static readonly BindableProperty PagePositionTextProperty = BindableProperty.Create(
            nameof(PagePositionText), typeof(string), typeof(ViewIllustPage));
        public static readonly BindableProperty IsPageVisibleProperty = BindableProperty.Create(
            nameof(IsPageVisible), typeof(bool), typeof(ViewIllustPage));
        public static readonly BindableProperty IllustItemProperty = BindableProperty.Create(
            nameof(IllustItem), typeof(IllustItem), typeof(ViewIllustPage));

        public ImageSource IsFavorite => (ImageSource)GetValue(IsFavoriteProperty);

        public IllustDetailItem[] Illusts
        {
            get => (IllustDetailItem[])GetValue(IllustsProperty);
            set => SetValue(IllustsProperty, value);
        }
        public string PagePositionText
        {
            get => (string)GetValue(PagePositionTextProperty);
            set => SetValue(PagePositionTextProperty, value);
        }
        public bool IsPageVisible
        {
            get => (bool)GetValue(IsPageVisibleProperty);
            set => SetValue(IsPageVisibleProperty, value);
        }
        public IllustItem IllustItem
        {
            get => (IllustItem)GetValue(IllustItemProperty);
            set => SetValue(IllustItemProperty, value);
        }

        public int CurrentPage { get; private set; }

        private readonly IIllustCollectionPage collectionPage;
        private readonly object fontIconLove;
        private readonly object fontIconNotLove;

        public ViewIllustPage(IllustItem illust, IIllustCollectionPage page)
        {
            IllustItem = illust;
            collectionPage = page;
            BindingContext = this;

            fontIconLove = Application.Current.Resources[ThemeBase.FontIconLove];
            fontIconNotLove = Application.Current.Resources[ThemeBase.FontIconNotLove];
            if (page.Favorites != null)
            {
                SetValue(IsFavoriteProperty, page.Favorites.Any(i => i.Id == illust.Id)
                    ? fontIconLove
                    : fontIconNotLove);
            }

            InitializeComponent();

            if (illust != null)
            {
                LoadIllust(illust);
            }
        }

        public override void OnLoad()
        {
            OnOrientationChanged(CurrentOrientation);
        }

        private void LoadIllust(IllustItem illust)
        {
            if (illust == null)
            {
                return;
            }

            var items = new IllustDetailItem[illust.PageCount];
            if (items.Length > 1)
            {
                IsPageVisible = true;
                PagePositionText = $"1/{items.Length}";
            }
            else
            {
                IsPageVisible = false;
            }

            for (var i = 0; i < items.Length; i++)
            {
                items[i] = new IllustDetailItem();
                if (i == 0)
                {
                    items[i].Loading = true;
                    items[i].Image = illust.Image;
                }
            }

            Illusts = items;
            Task.Run(DoLoadImages);
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            Screen.SetHomeIndicatorAutoHidden(Shell.Current, true);
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();

            if (collectionPage is IllustDataCollectionPage)
            {
                var favorite = new IllustFavorite
                {
                    LastFavoriteUtc = DateTime.UtcNow,
                    Illusts = collectionPage.Favorites.ToArray()
                };
                Stores.SaveFavoritesIllusts(favorite);
            }
            Screen.SetHomeIndicatorAutoHidden(Shell.Current, false);
        }

        private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e)
        {
            var index = e.CurrentPosition;
            CurrentPage = index;
            var items = Illusts;
            var length = items.Length;
            PagePositionText = $"{index + 1}/{length}";

            var item = items[index];
            if (!item.Loading && item.Image == null)
            {
                Task.Run(() => DoLoadImage(index));
            }
            if (index < length - 1)
            {
                item = items[index + 1];
                if (!item.Loading && item.Image == null)
                {
                    Task.Run(() => DoLoadImage(index + 1));
                }
            }
        }

        private void DoLoadImages()
        {
            var pages = Stores.LoadIllustPageData(IllustItem.Id);
            if (pages == null)
            {
                App.DebugError("illustPage.load", $"failed to load illust page data, id: {IllustItem.Id}");
                return;
            }
            var items = Illusts;
            if (pages.body.Length > items.Length)
            {
                App.DebugPrint($"local page count ({items.Length}) is not equals the remote one ({pages.body.Length})");
                var tmp = new IllustDetailItem[pages.body.Length];
                items.CopyTo(items, 0);
                for (var i = items.Length; i < tmp.Length; i++)
                {
                    tmp[i] = new IllustDetailItem();
                }
                items = tmp;
            }

            for (var i = 0; i < items.Length; i++)
            {
                var item = items[i];
                var p = pages.body[i];
                item.PreviewUrl = p.urls.regular;
                item.OriginalUrl = p.urls.original;
            }

            DoLoadImage(0);
            if (items.Length > 1)
            {
                DoLoadImage(1);
            }
        }

        private void DoLoadImage(int index)
        {
            var items = Illusts;
            if (index < 0 || index >= items.Length)
            {
                App.DebugPrint($"invalid index: {index}");
                return;
            }

            var item = items[index];
            if (item.Loading || (index > 0 && item.Image != null))
            {
                App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}");
                return;
            }
            item.Loading = true;
            var image = Stores.LoadPreviewImage(item.PreviewUrl);
            if (image != null)
            {
                item.Image = image;
            }
            item.Loading = false;
        }

        private void Favorite_Clicked(object sender, EventArgs e)
        {
            if (collectionPage.Favorites == null)
            {
                return;
            }
            var index = collectionPage.Favorites.FindIndex(i => i.Id == IllustItem.Id);
            if (index < 0)
            {
                collectionPage.Favorites.Insert(0, IllustItem);
                IllustItem.IsFavorite = true;
                SetValue(IsFavoriteProperty, fontIconLove);
            }
            else
            {
                collectionPage.Favorites.RemoveAt(index);
                IllustItem.IsFavorite = false;
                SetValue(IsFavoriteProperty, fontIconNotLove);
            }
        }

        private async void Download_Clicked(object sender, EventArgs e)
        {
            var status = await Permissions.CheckStatusAsync<Permissions.Photos>();
            if (status != PermissionStatus.Granted)
            {
                status = await Permissions.RequestAsync<Permissions.Photos>();
                if (status != PermissionStatus.Granted)
                {
                    App.DebugPrint("access denied to gallery.");
                    return;
                }
            }

            var item = Illusts[CurrentPage];
            if (item.Downloading)
            {
                return;
            }
            item.Downloading = true;
            _ = Task.Run(() => DoLoadOriginalImage(item));
        }

        private void DoLoadOriginalImage(IllustDetailItem item)
        {
            var image = Stores.LoadIllustImage(item.OriginalUrl);
            if (image != null)
            {
                Device.BeginInvokeOnMainThread(async () =>
                {
                    var service = DependencyService.Get<IFileStore>();
                    var result = await service.SaveImageToGalleryAsync(image);

                    string message = result ?? ResourceHelper.SaveSuccess;
                    await DisplayAlert(ResourceHelper.Title, message, ResourceHelper.Ok);
                });
            }
            item.Downloading = false;
        }
    }

    public class IllustDetailItem : BindableObject
    {
        public static readonly BindableProperty ImageProperty = BindableProperty.Create(
            nameof(Image), typeof(ImageSource), typeof(IllustDetailItem));
        public static readonly BindableProperty LoadingProperty = BindableProperty.Create(
            nameof(Loading), typeof(bool), typeof(IllustDetailItem));
        public static readonly BindableProperty DownloadingProperty = BindableProperty.Create(
            nameof(Downloading), typeof(bool), typeof(IllustDetailItem));

        public ImageSource Image
        {
            get => (ImageSource)GetValue(ImageProperty);
            set => SetValue(ImageProperty, value);
        }
        public bool Loading
        {
            get => (bool)GetValue(LoadingProperty);
            set => SetValue(LoadingProperty, value);
        }
        public bool Downloading
        {
            get => (bool)GetValue(DownloadingProperty);
            set => SetValue(DownloadingProperty, value);
        }
        public string PreviewUrl { get; set; }
        public string OriginalUrl { get; set; }
    }
}
