{-# LANGUAGE CPP, ScopedTypeVariables, StandaloneDeriving, TypeFamilies, MultiParamTypeClasses, PatternGuards, GADTs, ConstraintKinds #-}

-------------------------------------------------------------------------------------------
--- TreeTrie, variation which allows matching on subtrees marked as a variable (kind of unification)
-------------------------------------------------------------------------------------------

{- |
A TreeTrie is a search structure where the key actually consists of a
tree of keys, represented as a list of layers in the tree, 1 for every
depth, starting at the top, which are iteratively used for searching.
The search structure for common path/prefixes is shared, the trie
branches to multiple corresponding to available children, length
equality of children is used in searching (should match)

The TreeTrie structure implemented in this module deviates from the
usual TreeTrie implementations in that it allows wildcard matches
besides the normal full match. The objective is to also be able to
retrieve values for which (at insertion time) it has been indicated that
part does not need full matching. This intentionally is similar to
unification, where matching on a variable will succeed for arbitrary
values. Unification is not the job of this TreeTrie implementation, but
by returning partial matches as well, a list of possible match
candidates is returned.
-}

module CHR.Data.TreeTrie
  ( -- * Key into TreeTrie
    PreKey1
  , Key

    -- * Keyable
  , TrTrKey
  , TreeTrieKeyable(..)
  , toTreeTrieKey

  , prekey1
  , prekey1Wild
  , prekey1Nil
  , prekey1Delegate
  , prekey1WithChildren
  , prekey1With2Children
  , prekey1WithChild

    -- * TreeTrie
  , TreeTrie
  , TTCtxt
  , emptyTreeTrie
  , empty
  , toListByKey, toList
  , fromListByKeyWith, fromList

    -- * Lookup
  , lookup
  , lookupResultToList
    -- * Properties/observations
  , isEmpty, null

    -- * Construction
  , singleton, singletonKeyable
  , unionWith, union, unionsWith, unions
  , insertByKeyWith, insertByKey

  )
  where

import           Prelude                    hiding (lookup,null)

import qualified Data.Set                   as Set
import qualified Data.Map                   as Map
import           Data.Maybe
import           Data.Monoid
import qualified Data.List                  as List
import           Data.Typeable(Typeable)

import           GHC.Generics

import           Control.Monad

import           CHR.Utils
import qualified CHR.Data.Lookup            as Lk

-- import           UHC.Util.Serialize
import           CHR.Pretty                 hiding (empty)
import qualified CHR.Pretty                 as PP
-- import           CHR.Data.AssocL
import qualified CHR.Data.FastSeq           as Seq

-------------------------------------------------------------------------------------------
--- Key AST, used to index into TreeTrie
-------------------------------------------------------------------------------------------

-- | Key used on 1 level of trie.
-- Key1_Wild plays a special role, may occur in Key1_Multi only, and in there it is guaranteed to have non wild siblings, allowing easy wildcard lookup where only not all elements of the group need be a specific Key1_Single
data Key1 k
  = Key1_Single !k
  | Key1_Multi  ![Key1 k]
  | Key1_Wild                   -- ^ equal to anything
  | Key1_Nil                    -- ^ equal to nothing, except Key1_Wild
  deriving (Generic, Typeable {-, Eq, Ord -})

instance Functor Key1 where
  fmap f (Key1_Single k) = Key1_Single $ f k
  fmap f (Key1_Multi ks) = Key1_Multi  $ map (fmap f) ks
  fmap _ Key1_Wild       = Key1_Wild
  fmap _ Key1_Nil        = Key1_Nil

{-
-}
instance Eq k => Eq (Key1 k) where
  Key1_Single k1 == Key1_Single k2 = k1  == k2
  Key1_Multi ks1 == Key1_Multi ks2 = ks1 == ks2
  Key1_Nil       == Key1_Nil       = True
  Key1_Wild      == _              = True
  _              == Key1_Wild      = True
  _              == _              = False

instance Ord k => Ord (Key1 k) where
  Key1_Single k1 `compare` Key1_Single k2 = k1  `compare` k2
  Key1_Multi ks1 `compare` Key1_Multi ks2 = ks1 `compare` ks2
  Key1_Nil       `compare` Key1_Nil       = EQ
  Key1_Wild      `compare` _              = EQ
  _              `compare` Key1_Wild      = EQ
  Key1_Nil       `compare` _              = LT
  _              `compare` Key1_Nil       = GT
  Key1_Single _  `compare` _              = LT
  _              `compare` Key1_Single _  = GT

instance Show k => Show (Key1 k) where
  show (Key1_Single k) = "(" ++ show k ++ ")"
  show (Key1_Multi ks) = "[" ++ (concat $ List.intersperse "," $ map show ks) ++ "]"
  show  Key1_Wild      = "*"
  show  Key1_Nil       = "_"

instance PP k => PP (Key1 k) where
  pp (Key1_Single k) = ppParens k
  pp (Key1_Multi ks) = ppBracketsCommas ks
  pp  Key1_Wild      = pp "*"
  pp  Key1_Nil       = pp "_"

key1IsWild :: Key1 k -> Bool
key1IsWild Key1_Wild = True
key1IsWild _         = False
{-# INLINE key1IsWild #-}

-- | Full key
newtype Key k = Key {unKey :: [Key1 k]}
  deriving (Eq, Ord, Generic, Typeable, Show)

instance PP k => PP (Key k) where
  pp = ppBracketsCommas . unKey

-- | Simplify a generated raw Key1 into its canonical form used for indexing
key1RawToCanon :: Key1 k -> Key1 k
key1RawToCanon k = case k of
    Key1_Multi ks
      | List.null ks  -> Key1_Nil
      | all iswld sks -> Key1_Wild
      | all issim sks -> Key1_Nil
      | [sk] <- sks   -> sk
      | otherwise     -> Key1_Multi sks
      where sks = map key1RawToCanon ks
    -- k | issimp k       -> Key1_Nil
    k                  -> k
  where -- issimp Key1_Wild = True
        isnil Key1_Nil  = True
        isnil _         = False
        iswld Key1_Wild = True
        iswld _         = False
        issim k         = isnil k || iswld k

-- | Simplify a generated raw Key into its canonical form used for indexing
keyRawToCanon :: Key k -> Key k
keyRawToCanon = Key . simp . unKey
  where simp ks = case ks of
          (k:ks) | Key1_Nil  <- kc -> []
                 | Key1_Wild <- kc -> [] -- [Key1_Wild] -- if only wild further subtree matching would succeed by def
                 | otherwise       -> kc : simp ks
            where kc = key1RawToCanon k
          _ -> []

-------------------------------------------------------------------------------------------
--- Keyable
-------------------------------------------------------------------------------------------

type family TrTrKey x :: *

type instance TrTrKey [x] = TrTrKey x
type instance TrTrKey (Maybe x) = TrTrKey x

data PreKey1Cont y where
  PreKey1Cont_None :: PreKey1Cont y
  PreKey1Cont      :: (TrTrKey y ~ TrTrKey x, TreeTrieKeyable x) =>  x  -> PreKey1Cont y
  PreKey1Cont2     :: (TrTrKey y ~ TrTrKey x1, TrTrKey y ~ TrTrKey x2, TreeTrieKeyable x1, TreeTrieKeyable x2) =>  x1 -> x2  -> PreKey1Cont y
  PreKey1Conts     :: (TrTrKey y ~ TrTrKey x, TreeTrieKeyable x) => [x] -> PreKey1Cont y

data PreKey1 x
  = PreKey1       (TrTrKey x) (PreKey1Cont x)
  | PreKey1_Deleg             (PreKey1Cont x)
  | PreKey1_Wild
  | PreKey1_Nil

-- | Keyable values, i.e. capable of yielding a TreeTrieKey for retrieval from a trie
class TreeTrieKeyable x where
  toTreeTriePreKey1 :: x -> PreKey1 x

toTreeTrieKey :: TreeTrieKeyable x => x -> Key (TrTrKey x)
toTreeTrieKey = keyRawToCanon . Key . mkx
  where nil   = repeat Key1_Nil
        mkx x = case toTreeTriePreKey1 x of
                  PreKey1 k     mbx -> Key1_Single k : cont mbx
                  PreKey1_Deleg mbx -> cont mbx
                  PreKey1_Wild      -> repeat Key1_Wild
                  PreKey1_Nil       -> nil
        cont :: PreKey1Cont y -> [Key1 (TrTrKey y)]
        cont c = case c of
          PreKey1Cont_None -> nil
          PreKey1Cont  x   -> mkx x
          PreKey1Cont2 x y -> zipWithN Key1_Multi [mkx x, mkx y]
          PreKey1Conts xs  -> zipWithN Key1_Multi $ map mkx xs

-- | Single key
prekey1 :: TrTrKey x -> PreKey1 x
prekey1 k = PreKey1 k PreKey1Cont_None

-- | Wildcard, matching anything
prekey1Wild :: PreKey1 x
prekey1Wild = PreKey1_Wild

-- | No key
prekey1Nil :: PreKey1 x
prekey1Nil = PreKey1_Nil

-- | No key, delegate to next layer
prekey1Delegate :: (TrTrKey y ~ TrTrKey x, TreeTrieKeyable y) => y -> PreKey1 x
prekey1Delegate c = PreKey1_Deleg (PreKey1Cont c)

-- | Key with single child
prekey1WithChild :: (TrTrKey y ~ TrTrKey x, TreeTrieKeyable y) => TrTrKey x -> y -> PreKey1 x
prekey1WithChild k c = PreKey1 k (PreKey1Cont c)

-- | Key with children
prekey1WithChildren :: (TrTrKey y ~ TrTrKey x, TreeTrieKeyable y) => TrTrKey x -> [y] -> PreKey1 x
prekey1WithChildren k cs = PreKey1 k (PreKey1Conts cs)

-- | Key with 2 children
prekey1With2Children :: (TrTrKey y1 ~ TrTrKey x, TrTrKey y2 ~ TrTrKey x, TreeTrieKeyable y1, TreeTrieKeyable y2) => TrTrKey x -> y1 -> y2 -> PreKey1 x
prekey1With2Children k c1 c2 = PreKey1 k (PreKey1Cont2 c1 c2)

-------------------------------------------------------------------------------------------
--- TreeTrie structure
-------------------------------------------------------------------------------------------

-- | Child structure
type TreeTrieChildren k v
  = Map.Map (Key1 k) (TreeTrie k v)

type TTCtxt a = (Ord a)

-- | The trie structure, branching out on (1) kind, (2) nr of children, (3) actual key
data TreeTrie k v
  = TreeTrie
      { ttrieMbVal       :: Maybe v                                                 -- value
      , ttrieSubs        :: !(TreeTrieChildren k v)                                    -- children
      }
 deriving (Typeable)

emptyTreeTrie, empty :: TTCtxt k => TreeTrie k v
emptyTreeTrie = TreeTrie Nothing Lk.empty

empty = emptyTreeTrie

instance (TTCtxt k, Show k, Show v) => Show (TreeTrie k v) where
  showsPrec _ t = showList $ toListByKey t

instance (TTCtxt k, PP k, PP v) => PP (TreeTrie k v) where
  pp t = ppBracketsCommasBlock $ map (\(a,b) -> a >#< ":" >#< b) $ toListByKey t

-------------------------------------------------------------------------------------------
--- Conversion
-------------------------------------------------------------------------------------------

-- Reconstruction of original key-value pairs.

toFastSeqSubsWith :: TTCtxt k => (Key k -> v -> v') -> TreeTrieChildren k v -> Seq.FastSeq v'
toFastSeqSubsWith mk ttries
  = mconcat
      [ toFastSeqWith (\(Key ks) v -> mk (Key $ k:ks) v) True t
      | (k,t) <- Lk.toList ttries
      ]

toFastSeqSubs :: TTCtxt k => TreeTrieChildren k v -> Seq.FastSeq (Key k, v)
toFastSeqSubs = toFastSeqSubsWith (,)

toFastSeqWith :: TTCtxt k => (Key k -> v -> v') -> Bool -> TreeTrie k v -> Seq.FastSeq v'
toFastSeqWith mk inclEmpty ttrie
  =           (case ttrieMbVal ttrie of
                 Just v | inclEmpty -> Seq.singleton $ mk (Key []) v
                 _                  -> Seq.empty
              )
    `mappend` toFastSeqSubsWith mk (ttrieSubs ttrie)

toFastSeq :: TTCtxt k => Bool -> TreeTrie k v -> Seq.FastSeq (Key k, v)
toFastSeq = toFastSeqWith (,)

toListByKey, toList :: TTCtxt k => TreeTrie k v -> [(Key k,v)]
toListByKey = Seq.toList . toFastSeq True

toList = toListByKey

fromListByKeyWith :: Ord k => (v -> v -> v) -> [(Key k,v)] -> TreeTrie k v
fromListByKeyWith cmb = unionsWith cmb . map (uncurry singleton)

fromListByKey :: Ord k => [(Key k,v)] -> TreeTrie k v
fromListByKey = unions . map (uncurry singleton)

fromListWith :: Ord k => (v -> v -> v) -> [(Key k,v)] -> TreeTrie k v
fromListWith cmb = fromListByKeyWith cmb

fromList :: Ord k => [(Key k,v)] -> TreeTrie k v
fromList = fromListByKey

-------------------------------------------------------------------------------------------
--- Lookup
-------------------------------------------------------------------------------------------

type LkRes v = (Seq.FastSeq v, Seq.FastSeq v, Maybe v)

-- | Lookup giving back possible precise result and values found whilst descending into trie (corresponding to wildcard in key in trie) and remaining when key is exhausted (corresponding to wildcard in key)
lookupWith :: Ord k => (Key k -> v -> v') -> Key k -> TreeTrie k v -> LkRes v'
lookupWith mkRes keys ttrie = case unKey keys of
    [] -> (mempty, toFastSeqWith mkRes True ttrie, fmap (mkRes $ Key []) $ ttrieMbVal ttrie)
    (k : ks)
       -> case Lk.lookup k $ ttrieSubs ttrie of
            Just ttrie'
              -> (pp `mappend` p, s, m)
              where (p, s, m) = lookupWith (\(Key ks) v -> mkRes (Key (k : ks)) v) (Key ks) ttrie'
            _ -> (pp, mempty, Nothing)
       where pp = maybe mempty (Seq.singleton . mkRes (Key [])) (ttrieMbVal ttrie)

-- | Lookup giving back possible precise result and values found whilst descending into trie (corresponding to wildcard in key in trie) and remaining when key is exhausted (corresponding to wildcard in key)
lookup :: Ord k => Key k -> TreeTrie k v -> LkRes v
lookup = lookupWith $ \_ v -> v

-- | Convert the lookup result to a list of results
lookupResultToList :: LkRes v -> [v]
lookupResultToList (p,s,mv) = maybeToList mv ++ Seq.toList (p `mappend` s)


-------------------------------------------------------------------------------------------
--- Observation
-------------------------------------------------------------------------------------------


isEmpty :: TTCtxt k => TreeTrie k v -> Bool
isEmpty ttrie
  =  isNothing (ttrieMbVal ttrie)
  && Lk.null  (ttrieSubs ttrie)

null :: TTCtxt k => TreeTrie k v -> Bool
null = isEmpty

-------------------------------------------------------------------------------------------
--- Construction
-------------------------------------------------------------------------------------------

singleton :: Ord k => Key k -> v -> TreeTrie k v
singleton (Key keys) val
  = s keys
  where s []       = TreeTrie (Just val) Lk.empty
        s (k : ks) = TreeTrie Nothing (Lk.singleton k $ singleton (Key ks) val)

singletonKeyable :: (Ord (TrTrKey v),TreeTrieKeyable v) => v -> TreeTrie (TrTrKey v) v
singletonKeyable val = singleton (toTreeTrieKey val) val

-------------------------------------------------------------------------------------------
--- Union, insert, ...
-------------------------------------------------------------------------------------------

unionWith :: Ord k => (v -> v -> v) -> TreeTrie k v -> TreeTrie k v -> TreeTrie k v
unionWith cmb t1 t2
  = TreeTrie
      { ttrieMbVal       = mkMb          cmb             (ttrieMbVal t1) (ttrieMbVal t2)
      , ttrieSubs        = Lk.unionWith (unionWith cmb) (ttrieSubs  t1) (ttrieSubs  t2)
      }
  where mkMb _   j         Nothing   = j
        mkMb _   Nothing   j         = j
        mkMb cmb (Just x1) (Just x2) = Just $ cmb x1 x2

union :: Ord k => TreeTrie k v -> TreeTrie k v -> TreeTrie k v
union = unionWith const

unionsWith :: Ord k => (v -> v -> v) -> [TreeTrie k v] -> TreeTrie k v
unionsWith cmb [] = emptyTreeTrie
unionsWith cmb ts = foldr1 (unionWith cmb) ts

unions :: Ord k => [TreeTrie k v] -> TreeTrie k v
unions = unionsWith const

insertByKeyWith :: Ord k => (v -> v -> v) -> Key k -> v -> TreeTrie k v -> TreeTrie k v
insertByKeyWith cmb keys val ttrie = unionsWith cmb [singleton keys val,ttrie]

insertByKey :: Ord k => Key k -> v -> TreeTrie k v -> TreeTrie k v
insertByKey = insertByKeyWith const

insert :: Ord k => Key k -> v -> TreeTrie k v -> TreeTrie k v
insert = insertByKey

insertKeyable :: (Ord (TrTrKey v),TreeTrieKeyable v) => v -> TreeTrie (TrTrKey v) v -> TreeTrie (TrTrKey v) v
insertKeyable val = insertByKey (toTreeTrieKey val) val

-------------------------------------------------------------------------------------------
--- Instances: Serialize
-------------------------------------------------------------------------------------------

{-
instance (Ord k, Serialize k, Serialize v) => Serialize (TreeTrie k v) where
  sput (TreeTrie a b) = sput a >> sput b
  sget = liftM2 TreeTrie sget sget
  
instance (Serialize k) => Serialize (Key k) where
  sput (Key a) = sput a
  sget = liftM Key sget
  
instance (Serialize k) => Serialize (Key1 k)
-}