using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace Pilz.Cryptography
{
    [JsonConverter(typeof(Json.Converters.UniquieIDStringJsonConverter))]
    public class UniquieID : IUniquieID
    {
        protected static ulong currentSimpleID = 0;

        [JsonProperty(nameof(ID))]
        protected string _iD;

        [JsonIgnore]
        public virtual bool SimpleMode { get; }

        [JsonIgnore]
        public virtual bool GenerateOnGet { get; }

        [JsonIgnore]
        public virtual bool HasID => !string.IsNullOrEmpty(_iD);

        [JsonIgnore]
        public virtual string ID
        {
            get
            {
                if (GenerateOnGet)
                    GenerateIfNull();
                return _iD;
            }
            internal set
                => _iD = value;
        }

        public UniquieID() : this(UniquieIDGenerationMode.None)
        {
        }

        public UniquieID(UniquieIDGenerationMode mode) : this(mode, false)
        {
        }

        public UniquieID(UniquieIDGenerationMode mode, bool simpleMode)
        {
            SimpleMode = simpleMode;

            if (mode == UniquieIDGenerationMode.GenerateOnInit)
                GenerateIfNull();
            else if (mode == UniquieIDGenerationMode.GenerateOnGet)
                GenerateOnGet = true;
        }

        [Obsolete]
        public UniquieID(bool autoGenerate) : this(autoGenerate ? UniquieIDGenerationMode.GenerateOnInit : UniquieIDGenerationMode.None)
        {
        }

        public virtual void Generate()
        {
            if (SimpleMode)
                ID = GenerateSimple();
            else
                ID = GenerateDefault();
        }

        protected virtual string GenerateSimple()
        {
            return new Random().Next().ToString() + DateTime.Now.ToString("yyyyMMddHHmmssfffffff") + currentSimpleID++.ToString();
        }

        protected virtual string GenerateDefault()
        {
            return Helpers.GenerateUniquieID<UniquieID>(currentSimpleID++.ToString());
        }

        public virtual void GenerateIfNull()
        {
            if (!HasID) Generate();
        }

        public override string ToString()
        {
            return ID;
        }

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

        public override bool Equals(object obj)
        {
            if (obj is UniquieID iD)
            {
                if (ReferenceEquals(obj, iD))
                    return true;
                else
                {
                    var leftHasID = iD.HasID;
                    var rightHasID = HasID;

                    if (!leftHasID && iD.GenerateOnGet)
                    {
                        iD.Generate();
                        leftHasID = iD.HasID;
                    }

                    if (!rightHasID && GenerateOnGet)
                    {
                        Generate();
                        rightHasID = HasID;
                    }

                    if (leftHasID && rightHasID)
                        return _iD.Equals(iD._iD);
                }
            }
            
            return base.Equals(obj);
        }

        public static implicit operator string(UniquieID id) => id.ID;
        public static implicit operator UniquieID(string id) => new UniquieID() { ID = id };
        public static implicit operator UniquieID(int id) => new UniquieID() { ID = Convert.ToString(id) };

        public static bool operator ==(UniquieID left, UniquieID right) => left.ID.Equals(right.ID);
        public static bool operator !=(UniquieID left, UniquieID right) => !left.ID.Equals(right.ID);
    }
}
