using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pilz.Cryptography
{
    [JsonConverter(typeof(Json.Converters.SecureStringJsonConverter))]
    public class SecureString
    {
        public static ICrypter DefaultCrypter { get; set; }
        public ICrypter Crypter { get; set; }
        public string EncryptedValue { get; set; }
        public string Value
        {
            get => GetCrypter()?.Decrypt(EncryptedValue);
            set => EncryptedValue = GetCrypter().Encrypt(value);
        }

        [JsonConstructor]
        private SecureString(JsonConstructorAttribute dummyAttribute)
        {
        }

        public SecureString() :
            this(string.Empty, true)
        {
        }

        public SecureString(string value, bool isEncrypted) :
            this(value, isEncrypted, null)
        {
        }

        public SecureString(string value, bool isEncrypted, ICrypter crypter)
        {
            Crypter = crypter;

            if (isEncrypted)
                EncryptedValue = value;
            else
                Value = value;
        }

        private ICrypter GetCrypter()
        {
            if (Crypter == null)
            {
                if (DefaultCrypter == null)
                    DefaultCrypter = new SimpleStringCrypter(string.Empty);
                Crypter = DefaultCrypter;
            }
            return Crypter;
        }

        public override string ToString() => Value;

        public override bool Equals(object obj)
        {
            var @string = obj as SecureString;
            return @string != null &&
                   EncryptedValue == @string.EncryptedValue;
        }

        public override int GetHashCode()
        {
            return -2303024 + EqualityComparer<string>.Default.GetHashCode(EncryptedValue);
        }

        public static implicit operator string(SecureString value) => value?.Value;
        public static implicit operator SecureString(string value) => new SecureString(value, false);

        public static bool operator ==(SecureString left, SecureString right) => left?.EncryptedValue == right?.EncryptedValue;
        public static bool operator !=(SecureString left, SecureString right) => left?.EncryptedValue != right?.EncryptedValue;
    }
}
