module HaskellWorks.Data.SegmentSet.Naive
  ( empty
  , fromList
  , remove
  , toList
  , update
  , Segment(..)
  , SegmentSet(..)
  ) where

import Data.Foldable hiding (toList)
import Data.Maybe
import Debug.Trace

import HaskellWorks.Data.Segment.Strict

newtype SegmentSet a = SegmentSet [Segment a] deriving (Show, Eq)

empty :: SegmentSet a
empty = SegmentSet []

fromList :: (Ord a, Enum a) => [Segment a] -> SegmentSet a
fromList = foldr' update empty

toList :: SegmentSet a -> [Segment a]
toList (SegmentSet as) = as

update :: (Ord a, Enum a) => Segment a -> SegmentSet a -> SegmentSet a
update i (SegmentSet as) =
  let (ls, b1, b2, rs)  = splitSegment i as
      i'                = merge b1 i b2
   in SegmentSet $ ls ++ (i':rs)

remove :: (Ord a, Enum a) => Segment a -> SegmentSet a -> SegmentSet a
remove i (SegmentSet as) =
  let (ls, b1, b2, rs) = splitSegment i as
      b1s = maybe [] (minus i) b1
      b2s = maybe [] (minus i) b2
   in SegmentSet $ ls ++ b1s ++ b2s ++ rs

splitSegment :: (Ord a, Enum a) => Segment a -> [Segment a] -> ([Segment a], Maybe (Segment a), Maybe (Segment a), [Segment a])
splitSegment (Segment s e) as = (ls, b1, b2, rs)
  where (ls, xs ) = break (\(Segment x y) -> x >= s || y >= s) as
        (b1, xs') = unconsMergeable (Segment s e) xs
        (_ , rs') = break (\(Segment x y) -> x >= e || y >= e) xs'
        (b2, rs ) = unconsMergeable (Segment s e) rs'
        unconsMergeable ip ips = case uncons' ips of
          (Just b', rs'') | overlapsOrAdjacent ip b' -> (Just b', rs'')
          _               -> (Nothing, ips)

overlapsOrAdjacent :: (Ord a, Enum a) => Segment a -> Segment a -> Bool
overlapsOrAdjacent (Segment s1 e1) (Segment s2 e2) = if s1 <= s2 then succ e1 >= s2 else e2 >= s1

minus :: (Ord a, Enum a) => Segment a -> Segment a -> [Segment a]
minus (Segment s e) (Segment fs fe) =
  let as = if s <= fs then Nothing else Just (Segment  fs      (pred s))
      bs = if e >= fe then Nothing else Just (Segment (succ e)  fe     )
  in catMaybes [as, bs]

merge :: (Ord a, Enum a) => Maybe (Segment a) -> Segment a -> Maybe (Segment a) -> Segment a
merge (Just (Segment sb1 _  )) (Segment s e) (Just (Segment _   eb2)) = Segment (min sb1 s) (max e eb2)
merge Nothing                  (Segment s e) (Just (Segment sb2 eb2)) = Segment (min sb2 s) (max e eb2)
merge (Just (Segment sb1 eb2)) (Segment s e) Nothing                  = Segment (min sb1 s) (max e eb2)
merge _ i _                                                           = i

uncons' :: [a] -> (Maybe a, [a])
uncons' []     = (Nothing, [])
uncons' (a:as) = (Just a , as)