using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Sorting {
    /// 
    /// Class BaseItemComparer
    /// 
    public class BaseItemComparer : IComparer {
        /// 
        /// The _order
        /// 
        private readonly SortOrder _order;
        /// 
        /// The _property name
        /// 
        private readonly string _propertyName;
        /// 
        /// The _compare culture
        /// 
        private readonly StringComparison _compareCulture = StringComparison.CurrentCultureIgnoreCase;
        /// 
        /// Gets or sets the logger.
        /// 
        /// The logger.
        private ILogger Logger { get; set; }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The order.
        /// The logger.
        public BaseItemComparer(SortOrder order, ILogger logger) {
            _order = order;
            Logger = logger;
        }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The order.
        /// The compare.
        /// The logger.
        public BaseItemComparer(SortOrder order, StringComparison compare, ILogger logger)
        {
            _order = order;
            _compareCulture = compare;
            Logger = logger;
        }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The property.
        /// The logger.
        public BaseItemComparer(string property, ILogger logger)
        {
            _order = SortOrder.Custom;
            _propertyName = property;
            Logger = logger;
        }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The property.
        /// The compare.
        /// The logger.
        public BaseItemComparer(string property, StringComparison compare, ILogger logger)
        {
            _order = SortOrder.Custom;
            _propertyName = property;
            _compareCulture = compare;
            Logger = logger;
        }
        #region IComparer Members
        /// 
        /// Compares the specified x.
        /// 
        /// The x.
        /// The y.
        /// System.Int32.
        public int Compare(BaseItem x, BaseItem y) {
            int compare = 0;
            switch (_order) {
                case SortOrder.Date:
                    compare = -x.DateCreated.CompareTo(y.DateCreated);
                    break;
                case SortOrder.Year:
                    var xProductionYear = x.ProductionYear ?? 0;
                    var yProductionYear = y.ProductionYear ?? 0;
                    compare = yProductionYear.CompareTo(xProductionYear); 
                    break;
                case SortOrder.Rating:
                    var xRating = x.CommunityRating ?? 0;
                    var yRating = y.CommunityRating ?? 0;
                    compare = yRating.CompareTo(xRating);
                    break;
                case SortOrder.Runtime:
                    var xRuntime = x.RunTimeTicks ?? 0;
                    var yRuntime = y.RunTimeTicks ?? 0;
                    compare = xRuntime.CompareTo(yRuntime);
                    break;
                case SortOrder.Custom:
                    Logger.Debug("Sorting on custom field " + _propertyName);
                    var yProp = y.GetType().GetProperty(_propertyName);
                    var xProp = x.GetType().GetProperty(_propertyName);
                    if (yProp == null || xProp == null) break;
                    var yVal = yProp.GetValue(y, null);
                    var xVal = xProp.GetValue(x,null);
                    if (yVal == null && xVal == null) break;
                    if (yVal == null) return 1;
                    if (xVal == null) return -1;
                    compare = String.Compare(xVal.ToString(), yVal.ToString(),_compareCulture);
                    break;
                default:
                    compare = 0;
                    break;
            }
            if (compare == 0) {
                var name1 = x.SortName ?? x.Name ?? "";
                var name2 = y.SortName ?? y.Name ?? "";
                //if (Config.Instance.EnableAlphanumericSorting)
                    compare = AlphaNumericCompare(name1, name2,_compareCulture);
                //else
                //    compare = String.Compare(name1,name2,_compareCulture);
            }
            return compare;
        }
        #endregion
        /// 
        /// Alphas the numeric compare.
        /// 
        /// The s1.
        /// The s2.
        /// The compare culture.
        /// System.Int32.
        private int AlphaNumericCompare(string s1, string s2, StringComparison compareCulture) {
            // http://dotnetperls.com/Content/Alphanumeric-Sorting.aspx
            int len1 = s1.Length;
            int len2 = s2.Length;
            int marker1 = 0;
            int marker2 = 0;
            // Walk through two the strings with two markers.
            while (marker1 < len1 && marker2 < len2) {
                char ch1 = s1[marker1];
                char ch2 = s2[marker2];
                // Some buffers we can build up characters in for each chunk.
                var space1 = new char[len1];
                var loc1 = 0;
                var space2 = new char[len2];
                var loc2 = 0;
                // Walk through all following characters that are digits or
                // characters in BOTH strings starting at the appropriate marker.
                // Collect char arrays.
                do {
                    space1[loc1++] = ch1;
                    marker1++;
                    if (marker1 < len1) {
                        ch1 = s1[marker1];
                    } else {
                        break;
                    }
                } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
                do {
                    space2[loc2++] = ch2;
                    marker2++;
                    if (marker2 < len2) {
                        ch2 = s2[marker2];
                    } else {
                        break;
                    }
                } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
                // If we have collected numbers, compare them numerically.
                // Otherwise, if we have strings, compare them alphabetically.
                var str1 = new string(space1);
                var str2 = new string(space2);
                
                var result = 0;
                //biggest int - 2147483647
                if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]) /*&& str1.Length < 10 && str2.Length < 10*/) //this assumed the entire string was a number...
                {
                    int thisNumericChunk;
                    var isValid = false;
                    if (int.TryParse(str1.Substring(0, str1.Length > 9 ? 10 : str1.Length), out thisNumericChunk))
                    {
                        int thatNumericChunk;
                        
                        if (int.TryParse(str2.Substring(0, str2.Length > 9 ? 10 : str2.Length), out thatNumericChunk))
                        {
                            isValid = true;
                            result = thisNumericChunk.CompareTo(thatNumericChunk);
                        }
                    }
                    
                    if (!isValid)
                    {
                        Logger.Error("Error comparing numeric strings: " + str1 + "/" + str2);
                        result = String.Compare(str1, str2, compareCulture);
                    }
                    
                } else {
                    result = String.Compare(str1,str2,compareCulture);
                }
                if (result != 0) {
                    return result;
                }
            }
            return len1 - len2;
        }
    }
}