{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE ScopedTypeVariables #-}

module HaskellWorks.Data.SegmentMap.StrictSpec
  ( spec
  ) where

import Data.Foldable

import Control.Monad.IO.Class
import Data.Semigroup
import HaskellWorks.Data.FingerTree.Strict (ViewL (..), ViewR (..), viewl, viewr, (<|), (><), (|>))
import HaskellWorks.Data.SegmentMap.Strict

import qualified HaskellWorks.Data.FingerTree.Strict as FT
import qualified HaskellWorks.Data.SegmentMap.Strict as S (fromList)
import qualified Hedgehog.Gen                        as Gen
import qualified Hedgehog.Range                      as Range

import HaskellWorks.Hspec.Hedgehog
import Hedgehog
import Test.Hspec

{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}

fallbackTo :: Bool
fallbackTo = True

spec :: Spec
spec = describe "HaskellWorks.Data.SegmentMap.StrictSpec" $ do
    it "should convert SegmentMap to List" $ do
      let emptySM :: SegmentMap Int Int = empty
      segmentMapToList emptySM `shouldBe` []

    it "should convert List to SegmentMap" $ do
      let emptySM :: SegmentMap Int Int = empty
      let emptySM2 :: SegmentMap Int Int = S.fromList []
      segmentMapToList emptySM2 `shouldBe` segmentMapToList emptySM

    it "fromList with no overlap works" $ do
      let initial = fromList [(Segment 1 10, "1-10"), (Segment 11 20, "11-20")] :: SegmentMap Int String
      let expected = [(Segment 1 10, "1-10"), (Segment 11 20, "11-20")]
      segmentMapToList initial `shouldBe` expected

    it "insert with overlap works" $ do
      let initial = fromList [(Segment 1 10, "A"), (Segment 21 30, "C")] :: SegmentMap Int String
      let updated = insert (Segment 11 20) "B" initial
      let expected = [(Segment 1 10, "A"), (Segment 11 20, "B"), (Segment 21 30, "C")]
      segmentMapToList updated `shouldBe` expected

    it "insert with overlap works" $ do
      let initial = fromList [(Segment 1 10, "A"), (Segment 11 20, "C")] :: SegmentMap Int String
      let updated = insert (Segment 5 15) "B" initial
      let expected = [(Segment 1 4, "A"), (Segment 5 15, "B"), (Segment 16 20, "C")]
      segmentMapToList updated `shouldBe` expected

    it "fromList of two segments in order possibly overlapping" $ do
      require $ property $ do
        (Segment aLt aRt, Segment bLt bRt) <- forAll $ do
          aLt <- Gen.int (Range.linear 1   100)
          bRt <- Gen.int (Range.linear aLt 100)
          aRt <- Gen.int (Range.linear aLt bRt)
          bLt <- Gen.int (Range.linear aLt bRt)
          return (Segment aLt aRt, Segment bLt bRt)
        let initial = [(Segment aLt aRt, "A"), (Segment bLt bRt, "B")] :: [(Segment Int, String)]
        let actual = segmentMapToList (fromList initial)
        let aRt' = aRt `min` pred bLt
        case () of
          () | aLt == bRt               -> actual === [(Segment aLt bRt , "B")]
          () | bLt <= aLt && bRt >= aRt -> actual === [(Segment bLt bRt , "B")]
          () | aRt >= bLt               -> actual === [(Segment aLt aRt', "A"), (Segment bLt bRt, "B")]
          () | fallbackTo               -> actual === [(Segment aLt aRt , "A"), (Segment bLt bRt, "B")]

    it "fromList [(Segment 1 1, \"A\"), (Segment 1 1, \"B\")]" $ do
      let initial = [(Segment 1 1, "A"), (Segment 1 1, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "B")]

    it "fromList [(Segment 1 2, \"A\"), (Segment 1 1, \"B\")]" $ do
      let initial = [(Segment 1 2, "A"), (Segment 1 1, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "B"), (Segment 2 2, "A")]

    it "fromList [(Segment 1 2, \"A\"), (Segment 2 2, \"B\")]" $ do
      let initial = [(Segment 1 2, "A"), (Segment 2 2, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "A"), (Segment 2 2, "B")]

    it "fromList [(Segment 1 2, \"A\"), (Segment 1 2, \"B\")]" $ do
      let initial = [(Segment 1 2, "A"), (Segment 1 2, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 2, "B")]

    it "fromList [(Segment 1 3, \"A\"), (Segment 1 1, \"B\")]" $ do
      let initial = [(Segment 1 3, "A"), (Segment 1 1, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "B"), (Segment 2 3, "A")]

    it "fromList [(Segment 1 3, \"A\"), (Segment 3 3, \"B\")]" $ do
      let initial = [(Segment 1 3, "A"), (Segment 3 3, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 2, "A"), (Segment 3 3, "B")]

    it "fromList [(Segment 1 3, \"A\"), (Segment 2 2, \"B\")]" $ do
      let initial = [(Segment 1 3, "A"), (Segment 2 2, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "A"), (Segment 2 2, "B"), (Segment 3 3, "A")]

    it "fromList [(Segment 1 3, \"A\"), (Segment 0 1, \"B\")]" $ do
      let initial = [(Segment 1 3, "A"), (Segment 0 1, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 0 1, "B"), (Segment 2 3, "A")]

    it "fromList [(Segment 1 3, \"A\"), (Segment 3 4 \"B\")]" $ do
      let initial = [(Segment 1 3, "A"), (Segment 3 4, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 2, "A"), (Segment 3 4, "B")]

    it "fromList [(Segment 1 2, \"A\"), (Segment 2 7, \"B\")]" $ do
      let initial = [(Segment 1 2, "A"), (Segment 2 7, "B")] :: [(Segment Int, String)]
      let actual = segmentMapToList (fromList initial)
      actual `shouldBe` [(Segment 1 1, "A"), (Segment 2 7, "B")]

    describe "cappedL" $ do
      let original = FT.Single (Item (Max (11  :: Int)) (Segment {low = 11 :: Int, high = 20}, "A" :: String))
      it "left of" $ do
        cappedL  5 original `shouldBe` (FT.Empty, FT.Empty)
      it "overlapping" $ do
        cappedL 15 original `shouldBe` (FT.Single (Item (Max (11  :: Int)) (Segment {low = 11 :: Int, high = 14}, "A" :: String)), FT.Single (Item (Max 15) (Segment {low = 15, high = 20}, "A")))
      it "right of" $ do
        cappedL 25 original `shouldBe` (FT.Single (Item (Max (11  :: Int)) (Segment {low = 11 :: Int, high = 20}, "A" :: String)), FT.Empty)
    describe "cappedM" $ do
      let original = FT.Single (Item (Max (21 :: Int)) (Segment {low = 21 :: Int, high = 30}, "C" :: String))
      it "left of" $ do
        cappedM 15 original `shouldBe` FT.Single (Item (Max (21 :: Int)) (Segment {low = 21 :: Int, high = 30}, "C" :: String))
      it "overlapping" $ do
        cappedM 25 original `shouldBe` FT.Single (Item (Max (26 :: Int)) (Segment {low = 26 :: Int, high = 30}, "C" :: String))
      it "left of" $ do
        cappedM 35 original `shouldBe` FT.Empty

    it "should have require function that checks hedgehog properties" $ do
      require $ property $ do
        x <- forAll (Gen.int Range.constantBounded)
        x === x