using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.Collections {
  /// 
  /// Non-generic class with generic methods which proxy to the non-generic methods
  /// in the generic class.
  /// 
  public static class Dictionaries {
    /// 
    /// Compares two dictionaries for equality. Each value is compared with equality using Equals
    /// for non-IEnumerable implementations, and using EnumerableEquals otherwise.
    /// TODO(jonskeet): This is clearly pretty slow, and involves lots of boxing/unboxing...
    /// 
    public static bool Equals(IDictionary left, IDictionary right) {
      if (left.Count != right.Count) {
        return false;
      }
      foreach (KeyValuePair leftEntry in left)
      {
        TValue rightValue;
        if (!right.TryGetValue(leftEntry.Key, out rightValue)) {
          return false;
        }
        IEnumerable leftEnumerable = leftEntry.Value as IEnumerable;
        IEnumerable rightEnumerable = rightValue as IEnumerable;
        if (leftEnumerable == null || rightEnumerable == null) {
          if (!object.Equals(leftEntry.Value, rightValue)) {
            return false;
          }
        } else {
          IEnumerator leftEnumerator = leftEnumerable.GetEnumerator();
          try {
            foreach (object rightObject in rightEnumerable) {
              if (!leftEnumerator.MoveNext()) {
                return false;
              }
              if (!object.Equals(leftEnumerator.Current, rightObject)) {
                return false;
              }
            }
            if (leftEnumerator.MoveNext()) {
              return false;
            }
          } finally {
            if (leftEnumerator is IDisposable) {
              ((IDisposable)leftEnumerator).Dispose();
            }
          }
        }
      }
      return true;
    }
    public static IDictionary AsReadOnly (IDictionary dictionary) {
      return dictionary.IsReadOnly ? dictionary : new ReadOnlyDictionary(dictionary);
    }
    /// 
    /// Creates a hashcode for a dictionary by XORing the hashcodes of all the fields
    /// and values. (By XORing, we avoid ordering issues.)
    /// TODO(jonskeet): Currently XORs other stuff too, and assumes non-null values.
    /// 
    public static int GetHashCode(IDictionary dictionary) {
      int ret = 31;
      foreach (KeyValuePair entry in dictionary) {
        int hash = entry.Key.GetHashCode() ^ GetDeepHashCode(entry.Value);
        ret ^= hash;
      }
      return ret;
    }
    /// 
    /// Determines the hash of a value by either taking it directly or hashing all the elements
    /// for IEnumerable implementations.
    /// 
    private static int GetDeepHashCode(object value) {
      IEnumerable iterable = value as IEnumerable;
      if (iterable == null) {
        return value.GetHashCode();
      }
      int hash = 29;
      foreach (object element in iterable) {
        hash = hash * 37 + element.GetHashCode();
      }
      return hash;
    }
  }
}