{-# LANGUAGE OverloadedLists #-}

module Spec.Multimap (
  listMultimapSpec,
  seqMultimapSpec,
  setMultimapSpec
) where

import Data.Binary (decode, encode)
import Data.Char (toUpper)
import qualified Data.List as List
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Test.Hspec (Spec, describe, it, shouldBe)

import Data.Multimap
import Data.Multimap.List
import Data.Multimap.Seq
import qualified Data.Multimap.Set as SetMmap

listMultimapSpec :: Spec
listMultimapSpec = describe "ListMultimap" $ do
  it "should cons" $ do
    let m = fromGroupList [(1, "abc"), (2, "")]
    cons 3 'a' m `shouldBe` fromGroupList [(1, "abc"), (3, "a")]
  it "should uncons" $ do
    let m = fromGroupList [(1, "aa")]
    uncons 1 m `shouldBe` Just ('a', singleton 1 'a')
    uncons 2 m `shouldBe` Nothing
  it "can be traversed" $ do
    let
      m1 = [('a', Just 1), ('a', Just 2)] :: ListMultimap Char (Maybe Int)
      m2 = [('b', Nothing)] :: ListMultimap Char (Maybe Int)
      m3 = fromMap $ Map.singleton 'a' [1, 2]
    sequenceA m1 `shouldBe` Just m3
    sequenceA (m1 <> m2) `shouldBe` Nothing
  it "converts to set multimap" $ do
    let
      m1 = fromList [('a', 1), ('a', 1)] :: ListMultimap Char Int
      m2 = mapGroups (fmap Set.fromList) m1 :: SetMultimap Char Int
    m2 `shouldBe` fromList [('a', 1)]
  it "supports min and max views" $ do
    let m = fromGroupList [(1, "ab"), (2, "c")]
    minViewWith List.uncons m `shouldBe` Just ((1, 'a'), fromGroupList [(1, "b"), (2, "c")])
    maxViewWith List.uncons m `shouldBe` Just ((2, 'c'), fromGroupList [(1, "ab")])
    maxViewWith List.uncons (empty :: ListMultimap Int Char) `shouldBe` Nothing

seqMultimapSpec :: Spec
seqMultimapSpec = describe "SeqMultimap" $ do
  it "should pop" $ do
    let m = fromList [(1, 'a'), (1, 'b'), (2, 'c')] :: SeqMultimap Int Char
    popFirst 1 m `shouldBe` Just ('a', fromList [(1, 'b'), (2, 'c')])
    popLast 1 m `shouldBe` Just ('b', fromList [(1, 'a'), (2, 'c')])
    popFirst 2 m `shouldBe` Just ('c', fromList [(1, 'a'), (1, 'b')])
    popLast 3 m `shouldBe` Nothing

setMultimapSpec :: Spec
setMultimapSpec = describe "SetMultimap" $ do
  it "should map" $ do
    let m = fromList [(1, 'a'), (1, 'A')] :: SetMultimap Int Char
    size m `shouldBe` 2
    distinctSize m `shouldBe` 1
    SetMmap.map toUpper m `shouldBe` singleton 1 'A'
  it "should delete" $ do
    let m = singleton 2 'a'
    SetMmap.delete 1 'a' empty `shouldBe` empty
    SetMmap.delete 2 'a' m `shouldBe` empty
    SetMmap.delete 2 'b' m `shouldBe` m
    SetMmap.delete 1 'a' m `shouldBe` m
  it "should check membership" $ do
    let m = fromList [(1, 'a'), (2, 'b')] :: SetMultimap Int Char
    SetMmap.member' 1 'a' m `shouldBe` True
    SetMmap.member' 1 'b' m `shouldBe` False
    SetMmap.notMember' 1 'b' m `shouldBe` True
  it "can be serialized" $ do
    let
      m = fromList [(1, 'a'), (2, 'b')] :: SetMultimap Int Char
      bs = encode m
    decode bs `shouldBe` m