// Copyright (c) 2014, 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.

import "package:collection/collection.dart";
import "package:test/test.dart";

void main() {
  group("with an empty canonicalized map", () {
    CanonicalizedMap<int, String, String> map;

    setUp(() {
      map = new CanonicalizedMap(int.parse,
          isValidKey: (s) => new RegExp(r"^\d+$").hasMatch(s as String));
    });

    test("canonicalizes keys on set and get", () {
      map["1"] = "value";
      expect(map["01"], equals("value"));
    });

    test("get returns null for uncanonicalizable key", () {
      expect(map["foo"], isNull);
    });

    test("set affects nothing for uncanonicalizable key", () {
      map["foo"] = "value";
      expect(map["foo"], isNull);
      expect(map.containsKey("foo"), isFalse);
      expect(map.length, equals(0));
    });

    test("canonicalizes keys for addAll", () {
      map.addAll({"1": "value 1", "2": "value 2", "3": "value 3"});
      expect(map["01"], equals("value 1"));
      expect(map["02"], equals("value 2"));
      expect(map["03"], equals("value 3"));
    });

    test("uses the final value for addAll collisions", () {
      map.addAll({"1": "value 1", "01": "value 2", "001": "value 3"});
      expect(map.length, equals(1));
      expect(map["0001"], equals("value 3"));
    });

    test("clear clears the map", () {
      map.addAll({"1": "value 1", "2": "value 2", "3": "value 3"});
      expect(map, isNot(isEmpty));
      map.clear();
      expect(map, isEmpty);
    });

    test("canonicalizes keys for containsKey", () {
      map["1"] = "value";
      expect(map.containsKey("01"), isTrue);
      expect(map.containsKey("2"), isFalse);
    });

    test("containsKey returns false for uncanonicalizable key", () {
      expect(map.containsKey("foo"), isFalse);
    });

    test("canonicalizes keys for putIfAbsent", () {
      map["1"] = "value";
      expect(map.putIfAbsent("01", () => throw new Exception("shouldn't run")),
          equals("value"));
      expect(map.putIfAbsent("2", () => "new value"), equals("new value"));
    });

    test("canonicalizes keys for remove", () {
      map["1"] = "value";
      expect(map.remove("2"), isNull);
      expect(map.remove("01"), equals("value"));
      expect(map, isEmpty);
    });

    test("remove returns null for uncanonicalizable key", () {
      expect(map.remove("foo"), isNull);
    });

    test("containsValue returns whether a value is in the map", () {
      map["1"] = "value";
      expect(map.containsValue("value"), isTrue);
      expect(map.containsValue("not value"), isFalse);
    });

    test("isEmpty returns whether the map is empty", () {
      expect(map.isEmpty, isTrue);
      map["1"] = "value";
      expect(map.isEmpty, isFalse);
      map.remove("01");
      expect(map.isEmpty, isTrue);
    });

    test("isNotEmpty returns whether the map isn't empty", () {
      expect(map.isNotEmpty, isFalse);
      map["1"] = "value";
      expect(map.isNotEmpty, isTrue);
      map.remove("01");
      expect(map.isNotEmpty, isFalse);
    });

    test("length returns the number of pairs in the map", () {
      expect(map.length, equals(0));
      map["1"] = "value 1";
      expect(map.length, equals(1));
      map["01"] = "value 01";
      expect(map.length, equals(1));
      map["02"] = "value 02";
      expect(map.length, equals(2));
    });

    test("uses original keys for keys", () {
      map["001"] = "value 1";
      map["02"] = "value 2";
      expect(map.keys, equals(["001", "02"]));
    });

    test("uses original keys for forEach", () {
      map["001"] = "value 1";
      map["02"] = "value 2";

      var keys = [];
      map.forEach((key, value) => keys.add(key));
      expect(keys, equals(["001", "02"]));
    });

    test("values returns all values in the map", () {
      map.addAll(
          {"1": "value 1", "01": "value 01", "2": "value 2", "03": "value 03"});

      expect(map.values, equals(["value 01", "value 2", "value 03"]));
    });

    test("entries returns all key-value pairs in the map", () {
      map.addAll({
        "1": "value 1",
        "01": "value 01",
        "2": "value 2",
      });

      var entries = map.entries.toList();
      expect(entries[0].key, "01");
      expect(entries[0].value, "value 01");
      expect(entries[1].key, "2");
      expect(entries[1].value, "value 2");
    });

    test("addEntries adds key-value pairs to the map", () {
      map.addEntries([
        new MapEntry("1", "value 1"),
        new MapEntry("01", "value 01"),
        new MapEntry("2", "value 2"),
      ]);
      expect(map, {"01": "value 01", "2": "value 2"});
    });

    test("cast returns a new map instance", () {
      expect(map.cast<Pattern, Pattern>(), isNot(same(map)));
    });
  });

  group("CanonicalizedMap builds an informative string representation", () {
    var map;
    setUp(() {
      map = new CanonicalizedMap<int, String, dynamic>(int.parse,
          isValidKey: (s) => new RegExp(r"^\d+$").hasMatch(s as String));
    });

    test("for an empty map", () {
      expect(map.toString(), equals('{}'));
    });

    test("for a map with one value", () {
      map.addAll({"1": "value 1"});
      expect(map.toString(), equals('{1: value 1}'));
    });

    test("for a map with multiple values", () {
      map.addAll(
          {"1": "value 1", "01": "value 01", "2": "value 2", "03": "value 03"});
      expect(
          map.toString(), equals('{01: value 01, 2: value 2, 03: value 03}'));
    });

    test("for a map with a loop", () {
      map.addAll({"1": "value 1", "2": map});
      expect(map.toString(), equals('{1: value 1, 2: {...}}'));
    });
  });

  group("CanonicalizedMap.from", () {
    test("canonicalizes its keys", () {
      var map = new CanonicalizedMap.from(
          {"1": "value 1", "2": "value 2", "3": "value 3"}, int.parse);
      expect(map["01"], equals("value 1"));
      expect(map["02"], equals("value 2"));
      expect(map["03"], equals("value 3"));
    });

    test("uses the final value for collisions", () {
      var map = new CanonicalizedMap.from(
          {"1": "value 1", "01": "value 2", "001": "value 3"}, int.parse);
      expect(map.length, equals(1));
      expect(map["0001"], equals("value 3"));
    });
  });
}
