﻿// File name: CacheKey.cs
//
// Author(s): Alessio Parma <alessio.parma@gmail.com>
//
// The MIT License (MIT)
//
// Copyright (c) 2014-2018 Alessio Parma <alessio.parma@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
// OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;

namespace PommaLabs.KVLite
{
    /// <summary>
    ///   Represents a cache key, used to identify an item inside a partition.
    /// </summary>
    public readonly struct CacheKey : IEquatable<CacheKey>
    {
        /// <summary>
        ///   Used when receiving null, empty or blank keys.
        /// </summary>
        public const string NoValuePart = "__none__";

        /// <summary>
        ///   Cache key, represented as a string.
        /// </summary>
        private readonly string _value;

        /// <summary>
        ///   Builds a cache key using specified value.
        /// </summary>
        /// <param name="value">Value.</param>
        public CacheKey(string value)
        {
            _value = string.IsNullOrWhiteSpace(value) ? NoValuePart : value;
        }

        /// <summary>
        ///   Builds a cache key using specified value parts.
        /// </summary>
        /// <param name="valueParts">Value parts.</param>
        public CacheKey(params string[] valueParts)
        {
            _value = string.Join(".", valueParts.Select(vp => string.IsNullOrWhiteSpace(vp) ? NoValuePart : vp));
        }

        /// <summary>
        ///   Builds a cache key using specified value parts.
        /// </summary>
        /// <param name="valueParts">Value parts.</param>
        public CacheKey(params object[] valueParts)
        {
            _value = string.Join(".", valueParts.Select(vp => vp?.ToString()).Select(vp => string.IsNullOrWhiteSpace(vp) ? NoValuePart : vp));
        }

        /// <summary>
        ///   Cache key, represented as a string.
        /// </summary>
        public string Value => _value ?? NoValuePart;

        /// <summary>
        ///   Implicit conversion from string to cache key.
        /// </summary>
        /// <param name="value">Value.</param>
        public static implicit operator CacheKey(string value) => new CacheKey(value);

        /// <summary>
        ///   Implicit conversion from string parts to cache key.
        /// </summary>
        /// <param name="valueParts">Value parts.</param>
        public static implicit operator CacheKey(string[] valueParts) => new CacheKey(valueParts);

        /// <summary>
        ///   Implicit conversion from object parts to cache key.
        /// </summary>
        /// <param name="valueParts">Value parts.</param>
        public static implicit operator CacheKey(object[] valueParts) => new CacheKey(valueParts);

        /// <summary>
        ///   Implicit conversion from cache key to string.
        /// </summary>
        /// <param name="cacheKey">Cache key.</param>
        public static implicit operator string(CacheKey cacheKey) => cacheKey.Value;

        #region Equality members

        /// <summary>
        ///   Returns the hash code for this instance.
        /// </summary>
        /// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
        public override int GetHashCode() => EqualityComparer<string>.Default.GetHashCode(Value);

        /// <summary>
        ///   Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        ///   true if the current object is equal to the <paramref name="other">other</paramref>
        ///   parameter; otherwise, false.
        /// </returns>
        public bool Equals(CacheKey other) => Value == other.Value;

        /// <summary>
        ///   Indicates whether this instance and a specified object are equal.
        /// </summary>
        /// <param name="obj">The object to compare with the current instance.</param>
        /// <returns>
        ///   true if <paramref name="obj">obj</paramref> and this instance are the same type and
        ///   represent the same value; otherwise, false.
        /// </returns>
        public override bool Equals(object obj) => obj is CacheKey other && Equals(other);

        /// <summary>
        ///   Compares two objects.
        /// </summary>
        /// <param name="x">Left.</param>
        /// <param name="y">Right.</param>
        /// <returns>True if they are equal, false otherwise.</returns>
        public static bool operator ==(CacheKey x, CacheKey y) => x.Equals(y);

        /// <summary>
        ///   Compares two objects.
        /// </summary>
        /// <param name="x">Left.</param>
        /// <param name="y">Right.</param>
        /// <returns>True if they are not equal, false otherwise.</returns>
        public static bool operator !=(CacheKey x, CacheKey y) => !x.Equals(y);

        #endregion Equality members
    }
}