// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of _js_helper;

abstract class ConstantMap<K, V> implements Map<K, V> {
  bool get isEmpty => length == 0;

  bool get isNotEmpty => !isEmpty;

  String toString() => Maps.mapToString(this);

  _throwUnmodifiable() {
    throw new UnsupportedError("Cannot modify unmodifiable Map");
  }
  void operator []=(K key, V val) => _throwUnmodifiable();
  V putIfAbsent(K key, V ifAbsent()) => _throwUnmodifiable();
  V remove(K key) => _throwUnmodifiable();
  void clear() => _throwUnmodifiable();
  void addAll(Map<K, V> other) => _throwUnmodifiable();
}

// This class has no constructor. This is on purpose since the instantiation
// is shortcut by the compiler.
class ConstantStringMap<K, V> extends ConstantMap<K, V>
                              implements _symbol_dev.EfficientLength {
  final int length;
  // A constant map is backed by a JavaScript object.
  final _jsObject;
  final List<K> _keys;

  bool containsValue(V needle) {
    return values.any((V value) => value == needle);
  }

  bool containsKey(Object key) {
    if (key is! String) return false;
    if (key == '__proto__') return false;
    return jsHasOwnProperty(_jsObject, key);
  }

  V operator [](Object key) {
    if (key is! String) return null;
    if (!containsKey(key)) return null;
    return jsPropertyAccess(_jsObject, key);
  }

  void forEach(void f(K key, V value)) {
    _keys.forEach((key) => f(key, this[key]));
  }

  Iterable<K> get keys {
    return new _ConstantMapKeyIterable<K>(this);
  }

  Iterable<V> get values {
    return new MappedIterable<K, V>(_keys, (key) => this[key]);
  }
}

// This class has no constructor. This is on purpose since the instantiation
// is shortcut by the compiler.
class ConstantProtoMap<K, V> extends ConstantStringMap<K, V> {
  final V _protoValue;

  bool containsKey(Object key) {
    if (key == '__proto__') return true;
    return super.containsKey(key);
  }

  V operator [](Object key) {
    if (key == '__proto__') return _protoValue;
    return super[key];
  }
}

class _ConstantMapKeyIterable<K> extends IterableBase<K> {
  ConstantStringMap<K, dynamic> _map;
  _ConstantMapKeyIterable(this._map);

  Iterator<K> get iterator => _map._keys.iterator;
}

// This class has no constructor. This is on purpose since the instantiation
// is shortcut by the compiler.
class GeneralConstantMap<K, V> extends ConstantMap<K, V> {
  // [_jsData] holds a key-value pair list.
  final _jsData;

  // We cannot create the backing map on creation since hashCode interceptors
  // have not been defined when constants are created.
  Map<K, V> _getMap() {
    if (JS('bool', r'!this.$map')) {
      Map backingMap =
          new LinkedHashMap<K, V>(equals: identical, hashCode: objectHashCode);
      JS('', r'this.$map = #', fillLiteralMap(_jsData, backingMap));
    }
    return JS('Map', r'this.$map');
  }

  bool containsValue(V needle) {
    return _getMap().containsValue(needle);
  }

  bool containsKey(Object key) {
    return _getMap().containsKey(key);
  }

  V operator [](Object key) {
    return _getMap()[key];
  }

  void forEach(void f(K key, V value)) {
    _getMap().forEach(f);
  }

  Iterable<K> get keys {
    return _getMap().keys;
  }

  Iterable<V> get values {
    return _getMap().values;
  }

  int get length => _getMap().length;
}
