using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using GitCommands.Utils;
using JetBrains.Annotations;

namespace GitCommands
{
    [Serializable]
    public sealed class CommitTemplateItem : ISerializable
    {
        public string Name { get; set; }
        public string Text { get; set; }

        public CommitTemplateItem(string name, string text)
        {
            Name = name;
            Text = text;
        }

        public CommitTemplateItem()
        {
            Name = string.Empty;
            Text = string.Empty;
        }

        private CommitTemplateItem(SerializationInfo info, StreamingContext context)
        {
            Name = (string)info.GetValue("Name", typeof(string));
            Text = (string)info.GetValue("Text", typeof(string));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Name", Name);
            info.AddValue("Text", Text);
        }

        public static void SaveToSettings(CommitTemplateItem[] items)
        {
            string strVal = SerializeCommitTemplates(items);
            AppSettings.CommitTemplates = strVal ?? string.Empty;
        }

        [CanBeNull]
        public static CommitTemplateItem[] LoadFromSettings()
        {
            string serializedString = AppSettings.CommitTemplates;
            var templates = DeserializeCommitTemplates(serializedString, out var shouldBeUpdated);
            if (shouldBeUpdated)
            {
                SaveToSettings(templates);
            }

            return templates;
        }

        private static string SerializeCommitTemplates(CommitTemplateItem[] items)
        {
            return JsonSerializer.Serialize(items);
        }

        [CanBeNull]
        private static CommitTemplateItem[] DeserializeCommitTemplates(string serializedString, out bool shouldBeUpdated)
        {
            shouldBeUpdated = false;
            if (string.IsNullOrEmpty(serializedString))
            {
                return null;
            }

            CommitTemplateItem[] commitTemplateItem = null;
            try
            {
                commitTemplateItem = JsonSerializer.Deserialize<CommitTemplateItem[]>(serializedString);
            }
            catch
            {
                // do nothing
            }

            if (commitTemplateItem == null)
            {
                try
                {
                    int p = serializedString.IndexOf(':');
                    int length = Convert.ToInt32(serializedString.Substring(0, p));

                    byte[] memoryData = Convert.FromBase64String(serializedString.Substring(p + 1));
                    using (var rs = new MemoryStream(memoryData, 0, length))
                    {
                        var sf = new BinaryFormatter { Binder = new MoveNamespaceDeserializationBinder() };
                        commitTemplateItem = (CommitTemplateItem[])sf.Deserialize(rs);
                    }

                    shouldBeUpdated = true;
                }
                catch
                {
                    return null;
                }
            }

            return commitTemplateItem;
        }
    }

    public sealed class MoveNamespaceDeserializationBinder : SerializationBinder
    {
        private const string OldNamespace = "GitUI.CommandsDialogs.CommitDialog";
        private const string NewNamespace = "GitCommands";

        public override Type BindToType(string assemblyName, string typeName)
        {
            typeName = typeName.Replace(OldNamespace, NewNamespace);

            return Type.GetType($"{typeName}, {assemblyName}");
        }
    }
}