using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Linq;
using Billing.Languages;
using Billing.Models;
using Xamarin.Forms;

namespace Billing.UI
{
    public static partial class Definition
    {
        public static string PrimaryColorKey = "PrimaryColor";
        public static partial (string main, long build) GetVersion();
        public static partial string GetRegularFontFamily();
        public static partial string GetSemiBoldFontFamily();
        public static partial string GetBoldFontFamily();
        public static partial string GetBrandsFontFamily();
    }

    public static class ExtensionHelper
    {
        public static T Binding<T>(this T obj, BindableProperty property, string path, BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject
        {
            obj.SetBinding(property, path, mode, converter);
            return obj;
        }

        public static T HorizontalOptions<T>(this T view, LayoutOptions options) where T : View
        {
            if (view != null)
            {
                view.HorizontalOptions = options;
            }
            return view;
        }

        public static T VerticalOptions<T>(this T view, LayoutOptions options) where T : View
        {
            if (view != null)
            {
                view.VerticalOptions = options;
            }
            return view;
        }

        public static T Margin<T>(this T view, Thickness margin) where T : View
        {
            view.Margin = margin;
            return view;
        }

        public static T DynamicResource<T>(this T view, BindableProperty property, string key) where T : Element
        {
            view.SetDynamicResource(property, key);
            return view;
        }

        public static T GridColumn<T>(this T view, int column) where T : BindableObject
        {
            Grid.SetColumn(view, column);
            return view;
        }

        public static T GridRow<T>(this T view, int row) where T : BindableObject
        {
            Grid.SetRow(view, row);
            return view;
        }

        public static T GridColumnSpan<T>(this T view, int columnSpan) where T : BindableObject
        {
            Grid.SetColumnSpan(view, columnSpan);
            return view;
        }

        public static T GridRowSpan<T>(this T view, int rowSpan) where T : BindableObject
        {
            Grid.SetRowSpan(view, rowSpan);
            return view;
        }

        public static async Task ShowMessage(this Page page, string message, string title = null)
        {
            await page.DisplayAlert(title ?? page.Title, message, Resource.Ok);
        }

        public static async Task<bool> ShowConfirm(this Page page, string message)
        {
            var yes = Resource.Yes;
            var result = await page.DisplayActionSheet(message, Resource.No, yes);
            return result == yes;
        }

        public static DateTime LastMoment(this DateTime date)
        {
            // add 23:59:59.999...
            return date.AddTicks(863999999999);
        }
    }

    public static class ModelExtensionHelper
    {
        public static List<T> FromStream<T>(Stream stream) where T : IModel, new()
        {
            XDocument doc = XDocument.Load(stream);
            var root = doc.Root;
            var list = new List<T>();
            foreach (XElement ele in root.Elements("item"))
            {
                if (ele.Attribute("null")?.Value == "1")
                {
                    list.Add(default);
                }
                else
                {
                    T value = new();
                    value.OnXmlDeserialize(ele);
                    list.Add(value);
                }
            }
            return list;
        }

        public static void ToStream<T>(this IEnumerable<T> list, Stream stream) where T : IModel
        {
            XElement root = new("root");
            foreach (var t in list)
            {
                XElement item = new("item");
                if (t == null)
                {
                    item.Add(new XAttribute("null", 1));
                }
                else
                {
                    t.OnXmlSerialize(item);
                }
                root.Add(item);
            }

            XDocument doc = new(new XDeclaration("1.0", "utf-8", "yes"), root);
            doc.Save(stream, SaveOptions.DisableFormatting);
        }
    }

    public class Tap : IDisposable
    {
        private readonly static object sync = new();
        private static Tap instance;

        private bool busy = false;

        private Tap() { }

        public static bool IsBusy => instance?.busy ?? false;

        public static Tap Start()
        {
            lock (sync)
            {
                (instance ??= new Tap()).busy = true;
            }
            return instance;
        }

        public void Dispose()
        {
            lock (sync)
            {
                busy = false;
            }
        }
    }
}