{-# LANGUAGE Rank2Types, FlexibleContexts, TypeFamilies, MultiParamTypeClasses, FunctionalDependencies #-}

module Data.TrieMap.Regular.Class where

import Data.TrieMap.Sized
import Data.TrieMap.Applicative
import Data.TrieMap.TrieKey
-- import Data.TrieMap.Regular.Eq
import Data.TrieMap.Regular.Ord
import Data.TrieMap.CPair

-- import Data.Monoid

import Control.Applicative

type family TrieMapT (f :: * -> *) :: * -> * -> *

class OrdT f => TrieKeyT (f :: * -> *) (m :: * -> * -> *) | m -> f, f -> m where
	emptyT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => m k a
	nullT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => m k a -> Bool
	sizeT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> m k a -> Int
	lookupT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => f k -> m k a -> Maybe (a)
	lookupIxT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> f k -> m k a -> IndexPos (f k) a
	assocAtT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> Int -> m k a -> IndexPos (f k) a
-- 	updateAtT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> Round -> (Int -> f k -> a -> Maybe (a)) -> Int -> m k a -> m k a
	alterT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> (Maybe (a) -> Maybe (a)) -> f k -> m k a -> m k a
	alterLookupT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> (Maybe a -> CPair x (Maybe a)) -> f k -> m k a -> CPair x (m k a)
	traverseWithKeyT :: (TrieMapT f ~ m, TrieKey k (TrieMap k), Applicative t) => 
		Sized b -> (f k -> a -> t (b)) -> m k a -> t (m k b)
	foldWithKeyT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => 
		(f k -> a -> b -> b) -> m k a -> b -> b
	foldlWithKeyT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) =>
		(f k -> b -> a -> b) -> m k a -> b -> b
	mapEitherT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => 
		Sized b -> Sized c -> EitherMap (f k) (a) (b) (c) -> m k a -> (m k b, m k c)
	splitLookupT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> SplitMap (a) x -> f k ->
		m k a -> (m k a, Maybe x, m k a)
	unionT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> UnionFunc (f k) (a) ->
		m k a -> m k a -> m k a
	isectT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized c -> IsectFunc (f k) (a) (b) (c) ->
		m k a -> m k b -> m k c
	diffT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> DiffFunc (f k) (a) (b) ->
		m k a -> m k b -> m k a
	extractT :: (TrieMapT f ~ m, TrieKey k (TrieMap k), Alternative t) => 
		Sized a -> ExtractFunc t (m k a) (f k) a x
-- 	extractMinT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> ExtractFunc (f k) First a (m k a) x
-- 	extractMaxT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> ExtractFunc (f k) Last a (m k a) x
-- 	alterMinT, alterMaxT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> (f k -> a -> Maybe (a)) ->
-- 		m k a -> m k a
	isSubmapT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => LEq (a) (b) -> LEq (m k a) (m k b)
	fromListT, fromAscListT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> (f k -> a -> a -> a) ->
		[(f k, a)] -> m k a
	fromDistAscListT :: (TrieMapT f ~ m, TrieKey k (TrieMap k)) => Sized a -> [(f k, a)] -> m k a
	fromListT s f = foldr (\ (k, a) -> alterT s (Just . maybe a (f k a)) k) emptyT
	fromAscListT = fromListT
	fromDistAscListT s = fromAscListT s (const const)
-- 	alterLookupT s f k m = fmap (\ v' -> alterT s (const v') k m) (f (lookupT k m))
	alterT s f k m = cpSnd (alterLookupT s (cP () . f) k m)
-- 	updateAtT s f i m = case assocAtT s i m of
-- 		(i, k, a) -> alterT s (const (f i k a)) k m

-- mapWithKeyT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) =>
-- 	Sized b -> (f k -> a -> b) -> TrieMapT f k a -> TrieMapT f k b
-- mapWithKeyT s f m = unId (traverseWithKeyT s (\ k a -> Id (f k a)) m)

guardNullT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) => TrieMapT f k a -> Maybe (TrieMapT f k a)
guardNullT m
	| nullT m	= Nothing
	| otherwise	= Just m

assocsT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) => TrieMapT f k a -> [(f k, a)]
assocsT m = foldWithKeyT (\ k a -> ((k, a):)) m []

singletonT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) => Sized a -> f k -> a -> TrieMapT f k a
singletonT s k a = alterT s (const (Just a)) k emptyT

mapWithKeyT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) => 
	Sized b -> (f k -> a -> b) -> TrieMapT f k a -> TrieMapT f k b
mapWithKeyT s f m = unId (traverseWithKeyT s (Id .: f) m)

aboutT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k), Alternative t) =>
	(f k -> a -> t z) -> TrieMapT f k a -> t z
aboutT f m = cpFst <$> extractT (const 0) (\ k a -> fmap (flip cP Nothing) (f k a)) m

{-alterMinT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) =>
	Sized a -> (f k -> a -> Maybe a) -> TrieMapT f k a -> TrieMapT f k a
alterMinT s f m = maybe m snd (getFirst (extractMinT s (\ k a -> ((), f k a)) m))

alterMaxT :: (TrieKeyT f (TrieMapT f), TrieKey k (TrieMap k)) =>
	Sized a -> (f k -> a -> Maybe a) -> TrieMapT f k a -> TrieMapT f k a-}
-- alterMaxT s f m = maybe m snd (getLast (extractMaxT s (\ k a -> ((), f k a)) m))