module Data.SlowSeq where
import           Control.Lens (bimap)
import           Data.FingerTree(ViewL(..),ViewR(..))
import qualified Data.Foldable as F
import           Data.Maybe
import qualified Data.Sequence as S
import qualified Data.Sequence.Util as SU
data Key a = NoKey | Key { getKey :: a } deriving (Show,Eq,Ord)
instance Semigroup (Key a) where
  k <> NoKey = k
  _ <> k     = k
instance Monoid (Key a) where
  mempty = NoKey
  k `mappend` k' = k <> k'
liftCmp                     :: (a -> a -> Ordering) -> Key a -> Key a -> Ordering
liftCmp _   NoKey   NoKey   = EQ
liftCmp _   NoKey   (Key _) = LT
liftCmp _   (Key _) NoKey   = GT
liftCmp cmp (Key x) (Key y) = x `cmp` y
newtype OrdSeq a = OrdSeq { _asSeq :: S.Seq a }
                   deriving (Show,Eq)
instance Semigroup (OrdSeq a) where
  (OrdSeq s) <> (OrdSeq t) = OrdSeq $ s `mappend` t
instance Monoid (OrdSeq a) where
  mempty = OrdSeq mempty
  mappend = (<>)
instance Foldable OrdSeq where
  foldMap f = foldMap f . _asSeq
  null      = null . _asSeq
  length    = length . _asSeq
  minimum   = fromJust . lookupMin
  maximum   = fromJust . lookupMax
type Compare a = a -> a -> Ordering
insertBy                  :: Compare a -> a -> OrdSeq a -> OrdSeq a
insertBy cmp x (OrdSeq s) = OrdSeq $ l `mappend` (x S.<| r)
  where
    (l,r) = split (\v -> cmp v x `elem` [EQ, GT]) s
insert :: Ord a => a -> OrdSeq a -> OrdSeq a
insert = insertBy compare
deleteAllBy         :: Compare a -> a -> OrdSeq a -> OrdSeq a
deleteAllBy cmp x s = l <> r
  where
    (l,_,r) = splitBy cmp x s
    
    
splitBy                  :: Compare a -> a -> OrdSeq a -> (OrdSeq a, OrdSeq a, OrdSeq a)
splitBy cmp x (OrdSeq s) = (OrdSeq l, OrdSeq m', OrdSeq r)
  where
    (l, m) = split (\v -> cmp v x `elem` [EQ,GT]) s
    (m',r) = split (\v -> cmp v x == GT) m
splitOn :: Ord b => (a -> b) -> b -> OrdSeq a -> (OrdSeq a, OrdSeq a, OrdSeq a)
splitOn f x (OrdSeq s) = (OrdSeq l, OrdSeq m', OrdSeq r)
  where
    (l, m) = split (\v -> compare (f v) x `elem` [EQ,GT]) s
    (m',r) = split (\v -> compare (f v) x ==     GT)      m
splitMonotonic  :: (a -> Bool) -> OrdSeq a -> (OrdSeq a, OrdSeq a)
splitMonotonic p = bimap OrdSeq OrdSeq . split p . _asSeq
split :: (a -> Bool) -> S.Seq a -> (S.Seq a, S.Seq a)
split = SU.splitMonotone
deleteAll :: Ord a => a -> OrdSeq a -> OrdSeq a
deleteAll = deleteAllBy compare
fromListBy     :: Compare a -> [a] -> OrdSeq a
fromListBy cmp = foldr (insertBy cmp) mempty
fromListByOrd :: Ord a => [a] -> OrdSeq a
fromListByOrd = fromListBy compare
fromAscList' :: [a] -> OrdSeq a
fromAscList' = OrdSeq . S.fromList
lookupBy                  :: Compare a -> a -> OrdSeq a -> Maybe a
lookupBy cmp x s = let (_,m,_) = splitBy cmp x s in listToMaybe . F.toList $ m
memberBy        :: Compare a -> a -> OrdSeq a -> Bool
memberBy cmp x = isJust . lookupBy cmp x
mapMonotonic   :: (a -> b) -> OrdSeq a -> OrdSeq b
mapMonotonic f = fromAscList' . map f . F.toList
viewl :: OrdSeq a -> ViewL OrdSeq a
viewl = f . S.viewl . _asSeq
  where
    f S.EmptyL         = EmptyL
    f (x S.:< s)  = x :< OrdSeq s
viewr :: OrdSeq a -> ViewR OrdSeq a
viewr = f . S.viewr . _asSeq
  where
    f S.EmptyR    = EmptyR
    f (s S.:> x)  = OrdSeq s :> x
minView   :: OrdSeq a -> Maybe (a, OrdSeq a)
minView s = case viewl s of
              EmptyL   -> Nothing
              (x :< t) -> Just (x,t)
lookupMin :: OrdSeq a -> Maybe a
lookupMin = fmap fst . minView
maxView   :: OrdSeq a -> Maybe (a, OrdSeq a)
maxView s = case viewr s of
              EmptyR   -> Nothing
              (t :> x) -> Just (x,t)
lookupMax :: OrdSeq a -> Maybe a
lookupMax = fmap fst . maxView