using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Newtonsoft.Json;
using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.UI.Theme;
using Pixiview.Utils;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Pixiview.Illust
{
    public interface IIllustCollectionPage
    {
        List<IllustItem> Favorites { get; }
        IllustCollection IllustCollection { get; set; }
    }

    public abstract class IllustDataCollectionPage : IllustCollectionPage<IllustData> { }
    public abstract class FavoriteIllustCollectionPage : IllustCollectionPage<IllustItem[]> { }

    public abstract class IllustCollectionPage<T> : AdaptedPage, IIllustCollectionPage
    {
        #region - Properties -

        public static readonly BindableProperty IllustsProperty = BindableProperty.Create(
            nameof(Illusts), typeof(IllustCollection), typeof(IllustCollectionPage<T>));
        public static readonly BindableProperty ColumnsProperty = BindableProperty.Create(
            nameof(Columns), typeof(int), typeof(IllustCollectionPage<T>), 2);
        public static readonly BindableProperty LoadingProperty = BindableProperty.Create(
            nameof(Loading), typeof(bool), typeof(IllustCollectionPage<T>), propertyChanged: OnLoadingPropertyChanged);

        private static void OnLoadingPropertyChanged(BindableObject obj, object oldValue, object newValue)
        {
            var page = (IllustCollectionPage<T>)obj;
            var now = (bool)newValue;
            if (!page.loaded && now && Stores.NetworkAvailable)
            {
                page.loaded = true;
                Task.Run(() => page.DoLoadIllusts());
            }
        }

        public IllustCollection Illusts
        {
            get => (IllustCollection)GetValue(IllustsProperty);
            set => SetValue(IllustsProperty, value);
        }
        public int Columns
        {
            get => (int)GetValue(ColumnsProperty);
            set => SetValue(ColumnsProperty, value);
        }
        public bool Loading
        {
            get => (bool)GetValue(LoadingProperty);
            set => SetValue(LoadingProperty, value);
        }

        public List<IllustItem> Favorites { get; } = new List<IllustItem>();
        public IllustCollection IllustCollection { get; set; }

        protected virtual bool IsFavoriteVisible => true;

        #endregion

        protected bool loaded;

        private T illustData;
        private readonly ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Configs.MaxThreads };
        private readonly Command<IllustItem> commandIllustImageTapped;

        public IllustCollectionPage()
        {
            BindingContext = this;
            commandIllustImageTapped = new Command<IllustItem>(IllustImageTapped);

            var favorites = Stores.LoadFavoritesIllusts();
            if (favorites != null)
            {
                Favorites.AddRange(favorites.Illusts);
            }
        }

        private void IllustImageTapped(IllustItem illust)
        {
            Start(() => OnIllustImageTapped(illust));
        }

        #region - Overrides -

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

            Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
        }

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

            Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
        }

        private void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
        {
            if (e.NetworkAccess == NetworkAccess.Internet || e.NetworkAccess == NetworkAccess.ConstrainedInternet)
            {
                StartLoad();
            }
        }

        public override void OnOrientationChanged(Orientation orientation)
        {
            int columns;
            switch (orientation)
            {
                case Orientation.Portrait:
                    columns = 2;
                    break;
                case Orientation.PortraitUpsideDown:
                    columns = isPhone ? 4 : 2;
                    break;
                case Orientation.Unknown:
                case Orientation.LandscapeLeft:
                case Orientation.LandscapeRight:
                default:
                    columns = 4;
                    break;
            }
            if (Columns != columns)
            {
                App.DebugPrint($"change columns to {columns}");
                Columns = columns;
            }
            base.OnOrientationChanged(orientation);
        }

        #endregion

        protected abstract T DoLoadIllustData(bool force);
        protected abstract IEnumerable<IllustItem> DoGetIllustList(T data, ICommand command);
        protected virtual void OnIllustImageTapped(IllustItem illust)
        {
            var page = new ViewIllustPage(illust, this);
            Navigation.PushAsync(page);
        }

        protected void StartLoad(bool force = false)
        {
            if (force)
            {
                Loading = true;
                Task.Run(() => DoLoadIllusts(true));
            }
            else if (!loaded)
            {
                Loading = true;
            }
        }

        protected DataTemplate GetCardViewTemplate()
        {
            return new DataTemplate(() =>
            {
                return new CardView
                {
                    Padding = 0,
                    Margin = 0,
                    CornerRadius = 10,
                    ShadowColor = StyleDefinition.ColorLightShadow,
                    ShadowOffset = new Size(2, 2),
                    Content = new Grid
                    {
                        HorizontalOptions = LayoutOptions.Fill,
                        RowDefinitions =
                        {
                            new RowDefinition().Binding(RowDefinition.HeightProperty, nameof(IllustItem.ImageHeight)),
                            new RowDefinition { Height = GridLength.Auto },
                            new RowDefinition { Height = GridLength.Auto }
                        },
                        Children =
                        {
                            // image
                            new RoundImage
                            {
                                BackgroundColor = Color.LightGray,
                                CornerRadius = 10,
                                CornerMasks = CornerMask.Top,
                                HorizontalOptions = LayoutOptions.Fill,
                                Aspect = Aspect.AspectFill,
                                GestureRecognizers =
                                {
                                    new TapGestureRecognizer()
                                        .Binding(TapGestureRecognizer.CommandProperty, nameof(IllustItem.IllustTapped))
                                        .Binding(TapGestureRecognizer.CommandParameterProperty, ".")
                                }
                            }
                            .Binding(Image.SourceProperty, nameof(IllustItem.Image)),

                            // label: r-18
                            new RoundLabel
                            {
                                Text = ResourceHelper.R18,
                                BackgroundColor = StyleDefinition.ColorRedBackground,
                                Margin = new Thickness(6, 6, 0, 0),
                                Padding = new Thickness(6, 2),
                                CornerRadius = 4,
                                HorizontalOptions = LayoutOptions.Start,
                                VerticalOptions = LayoutOptions.Start,
                                FontSize = StyleDefinition.FontSizeMicro,
                                TextColor = Color.White
                            }
                            .Binding(IsVisibleProperty, nameof(IllustItem.IsRestrict)),

                            // label: pages
                            new RoundLabel
                            {
                                BackgroundColor = StyleDefinition.ColorDeepShadow,
                                Margin = new Thickness(0, 6, 6, 0),
                                Padding = new Thickness(6, 4),
                                CornerRadius = 6,
                                HorizontalOptions = LayoutOptions.End,
                                VerticalOptions = LayoutOptions.Start,
                                FontSize = StyleDefinition.FontSizeMicro,
                                TextColor = Color.White
                            }
                            .Binding(Label.TextProperty, nameof(IllustItem.PageCountText))
                            .Binding(IsVisibleProperty, nameof(IllustItem.IsPageVisible))
                            .DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily),

                            // label: title
                            new Label
                            {
                                Padding = new Thickness(8, 2),
                                HorizontalOptions = LayoutOptions.Start,
                                LineBreakMode = LineBreakMode.TailTruncation,
                                FontSize = StyleDefinition.FontSizeSmall
                            }
                            .Binding(Label.TextProperty, nameof(IllustItem.Title))
                            .DynamicResource(Label.TextColorProperty, ThemeBase.TextColor)
                            .GridRow(1),

                            // stacklayout: user
                            new StackLayout
                            {
                                Orientation = StackOrientation.Horizontal,
                                Padding = new Thickness(8, 0, 8, 8),
                                Children =
                                {
                                    // user icon
                                    new CircleImage
                                    {
                                        WidthRequest = 30,
                                        HeightRequest = 30,
                                        Aspect = Aspect.AspectFill
                                    }
                                    .Binding(Image.SourceProperty, nameof(IllustItem.ProfileImage)),

                                    // user name
                                    new Label
                                    {
                                        HorizontalOptions = LayoutOptions.FillAndExpand,
                                        VerticalOptions = LayoutOptions.Center,
                                        LineBreakMode = LineBreakMode.TailTruncation,
                                        FontSize = StyleDefinition.FontSizeMicro
                                    }
                                    .Binding(Label.TextProperty, nameof(IllustItem.UserName))
                                    .DynamicResource(Label.TextColorProperty, ThemeBase.SubTextColor),

                                    // label: favorite
                                    new Label
                                    {
                                        WidthRequest = 26,
                                        HorizontalOptions = LayoutOptions.End,
                                        HorizontalTextAlignment = TextAlignment.End,
                                        VerticalOptions = LayoutOptions.Center,
                                        FontSize = StyleDefinition.FontSizeSmall,
                                        TextColor = StyleDefinition.ColorRedBackground,
                                        Text = StyleDefinition.IconLove
                                    }
                                    .Binding(IsVisibleProperty, nameof(IllustItem.IsFavorite))
                                    .DynamicResource(Label.FontFamilyProperty, ThemeBase.IconSolidFontFamily)
                                }
                            }
                            .GridRow(2)
                        }
                    }
                }
                .DynamicResource(BackgroundColorProperty, ThemeBase.SubColor);
            });
        }

        #region - Illust Tasks -

        void DoLoadIllusts(bool force = false)
        {
            illustData = DoLoadIllustData(force);
            if (illustData == null)
            {
                //App.DebugError("illusts.load", "failed to load illusts data.");
                Loading = false;
                return;
            }

            var data = DoGetIllustList(illustData, commandIllustImageTapped).Where(i => i != null);

            var collection = new IllustCollection(data);
            if (IsFavoriteVisible)
            {
                foreach (var item in collection)
                {
                    item.IsFavorite = Favorites.Any(i => i.Id == item.Id);
                }
            }
            IllustCollection = collection;
            Illusts = collection;
            Loading = false;

            DoLoadImages(collection);
        }

        void DoLoadImages(IllustCollection collection)
        {
            Parallel.ForEach(collection, parallelOptions, illust =>
            {
                if (!collection.Running)
                {
                    return;
                }
                if (illust.ImageUrl != null)
                {
                    var url = Configs.GetThumbnailUrl(illust.ImageUrl);
                    var image = Stores.LoadThumbnailImage(url);
                    if (image != null)
                    {
                        illust.Image = image;
                    }
                }

                if (illust.ProfileUrl != null)
                {
                    var userImage = Stores.LoadUserProfileImage(illust.ProfileUrl);
                    if (userImage != null)
                    {
                        illust.ProfileImage = userImage;
                    }
                }
            });
        }

        #endregion
    }

    public class IllustCollection : List<IllustItem>
    {
        private static readonly object sync = new object();
        private static IllustCollection empty;

        public static IllustCollection Empty
        {
            get
            {
                if (empty == null)
                {
                    empty = new IllustCollection();
                }
                return empty;
            }
        }

        public IllustCollection() : base()
        {
            running = true;
        }
        public IllustCollection(IEnumerable<IllustItem> illusts) : base(illusts)
        {
            running = true;
        }

        private bool running;
        public bool Running
        {
            get => running;
            set
            {
                lock (sync)
                {
                    running = value;
                }
            }
        }
    }

    public class IllustFavorite
    {
        public DateTime LastFavoriteUtc { get; set; }
        public IllustItem[] Illusts { get; set; }
    }

    [JsonObject(MemberSerialization.OptIn)]
    public class IllustItem : BindableObject
    {
        public static readonly BindableProperty ImageProperty = BindableProperty.Create(
            nameof(Image), typeof(ImageSource), typeof(IllustItem));
        public static readonly BindableProperty ProfileImageProperty = BindableProperty.Create(
            nameof(ProfileImage), typeof(ImageSource), typeof(IllustItem));
        public static readonly BindableProperty ImageHeightProperty = BindableProperty.Create(
            nameof(ImageHeight), typeof(GridLength), typeof(IllustItem), GridLength.Auto);
        public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(
            nameof(IsFavorite), typeof(bool), typeof(IllustItem));

        public ImageSource Image
        {
            get => (ImageSource)GetValue(ImageProperty);
            set => SetValue(ImageProperty, value);
        }
        public ImageSource ProfileImage
        {
            get => (ImageSource)GetValue(ProfileImageProperty);
            set => SetValue(ProfileImageProperty, value);
        }
        public GridLength ImageHeight
        {
            get => (GridLength)GetValue(ImageHeightProperty);
            set => SetValue(ImageHeightProperty, value);
        }
        public bool IsFavorite
        {
            get => (bool)GetValue(IsFavoriteProperty);
            set => SetValue(IsFavoriteProperty, value);
        }
        public ICommand IllustTapped { get; set; }

        [JsonProperty]
        public string Id { get; set; }
        [JsonProperty]
        public string ImageUrl { get; set; }
        [JsonProperty]
        public string Title { get; set; }
        [JsonProperty]
        public bool IsRestrict { get; set; }
        [JsonProperty]
        public string ProfileUrl { get; set; }
        [JsonProperty]
        public string UserId { get; set; }
        [JsonProperty]
        public string UserName { get; set; }
        [JsonProperty]
        public int Width { get; set; }
        [JsonProperty]
        public int Height { get; set; }
        [JsonProperty]
        public int PageCount { get; set; }
        [JsonProperty]
        public double ImageHeightValue
        {
            get => ImageHeight.IsAuto ? -1 : ImageHeight.Value;
            set => ImageHeight = value > 0 ? value : GridLength.Auto;
        }

        public string PageCountText => $"{StyleDefinition.IconLayer} {PageCount}";
        public bool IsPageVisible => PageCount > 1;
    }
}
