using TINK.Model.Connector;
using Newtonsoft.Json;
using System;
using System.Runtime.Serialization;

namespace TINK.Model.Bike.BluetoothLock
{
    /// <summary> Locking states. </summary>
    public enum LockingState
    {
        Disconnected,

        /// <summary> Lock might be open, closed or something in between..</summary>
        Unknown,

        /// <summary> Lock is closed. </summary>
        Closed,

        /// <summary> Lock is open. </summary>
        Open
    }

    [DataContract]
    public class LockInfo : ILockInfo, IEquatable<LockInfo>
    {
        /// <summary> Identification number of bluetooth lock, 6-digits, second part of advertisement name.</summary>
        [DataMember]
        public int Id { get; private set; } = TextToLockItTypeHelper.INVALIDLOCKID;

        /// <summary> Identification GUID of bluetooth lock.</summary>
        [DataMember]
        public Guid Guid { get; private set; } = TextToLockItTypeHelper.INVALIDLOCKGUID;

        [DataMember]
        public byte[] UserKey { get; private set; }

        [DataMember]
        public byte[] AdminKey { get; private set; }

        [DataMember]
        public byte[] Seed { get; private set; }

        /// <summary> Locking state of bluetooth lock. </summary>
        [DataMember]
        public LockingState State { get; private set; } = LockingState.Disconnected;

        public bool IsIdValid => Id != TextToLockItTypeHelper.INVALIDLOCKID;

        public bool IsGuidValid => Guid != TextToLockItTypeHelper.INVALIDLOCKGUID;

        public override bool Equals(object obj) => this.Equals(obj as LockInfo); 

        public bool Equals(LockInfo other)
        {
            if (Object.ReferenceEquals(other, null)) return false;
            if (Object.ReferenceEquals(this, other)) return true;
            if (this.GetType() != other.GetType()) return false;

            return ToString() == other.ToString();
        }

        public override int GetHashCode()
        {
            return ToString().GetHashCode();
        }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }

        public static bool operator ==(LockInfo lhs, LockInfo rhs)
        {
            if (Object.ReferenceEquals(lhs, null)) 
                return Object.ReferenceEquals(rhs, null) ? true /*null == null = true*/: false;

            return lhs.Equals(rhs);
        }

        public static bool operator !=(LockInfo lhs, LockInfo rhs)
        {
            return !(lhs == rhs);
        }

        public class Builder
        {
            public Builder(LockInfo lockInfo = null)
            {
                if (lockInfo == null)
                {
                    return;
                }

                LockInfo = JsonConvert.DeserializeObject<LockInfo>(JsonConvert.SerializeObject(lockInfo));
            }

            private readonly LockInfo LockInfo = new LockInfo();

            public byte[] UserKey { get => LockInfo.UserKey; set => LockInfo.UserKey = value; } 
            
            public byte[] AdminKey { get => LockInfo.AdminKey; set => LockInfo.AdminKey = value; }
            
            public byte[] Seed { get => LockInfo.Seed; set => LockInfo.Seed = value; }

            public int Id { get => LockInfo.Id; set => LockInfo.Id = value; }

            public Guid Guid { get => LockInfo.Guid; set => LockInfo.Guid = value; }

             public LockingState State { get => LockInfo.State; set => LockInfo.State = value; }

            public LockInfo Build()
            {
                // Ensure consistency.
                if  ((UserKey?.Length > 0 ||  Seed?.Length > 0) 
                    && (UserKey?.Length == 0 || Seed?.Length == 0 || !LockInfo.IsIdValid))
                        throw new ArgumentException($"Can not build {typeof(LockInfo).Name}. Lock parameters must either be all know or all unknown.");
                
                if (UserKey == null) UserKey = new byte[0];
                if (AdminKey == null) AdminKey = new byte[0];
                if (Seed == null) Seed = new byte[0];

                return LockInfo;
            }
        }

    }
}