{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.Winery.Query (Query(..)
  , invalid
  , list
  , range
  , field
  , con
  , select) where

import Prelude hiding ((.), id)
import Control.Applicative
import Control.Category
import Data.Winery
import Data.Winery.Internal
import Data.Typeable
import qualified Data.Text as T

newtype Query a b = Query
  { runQuery :: Deserialiser [a] -> Deserialiser [b] }
  deriving Functor

instance Category Query where
  id = Query $ fmap id
  Query f . Query g = Query $ f . g

instance Applicative (Query a) where
  pure a = Query $ const $ pure [a]
  Query f <*> Query g = Query $ \d -> (<*>) <$> f d <*> g d

instance Alternative (Query a) where
  empty = Query $ const $ pure []
  Query f <|> Query g = Query $ \d -> (++) <$> f d <*> g d

invalid :: StrategyError -> Query a b
invalid = Query . const . Deserialiser . Plan . const . errorStrategy

list :: Query a a
list = Query $ \d -> concat <$> extractListBy d

range :: Int -> Int -> Query a a
range i j = Query $ \d -> (\(n, f) -> concatMap f [mod i n..mod j n])
  <$> extractArrayBy d

field :: Typeable a => T.Text -> Query a a
field name = Query $ \d -> extractFieldBy d name

con :: Typeable a => T.Text -> Query a a
con name = Query $ \d -> maybe [] id <$> extractConstructorBy d name

select :: Query a Bool -> Query a a
select qp = Query $ \d -> Deserialiser $ Plan $ \sch -> do
  p <- unwrapDeserialiser (runQuery qp d) sch
  dec <- unwrapDeserialiser d sch
  return $ \bs -> [x | and $ p bs, x <- dec bs]