content: {-# LANGUAGE ScopedTypeVariables, TypeOperators #-}
  {-# LANGUAGE TypeFamilies, FlexibleInstances, MultiParamTypeClasses #-}
  {-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
  -- | Utilities for creating selectors for non-record types.
  --   In general, you should really use record types for your tables and
  --   their record labels (i.e. #label) as selectors using
  --   the @OverloadedLabels@ extension instead.
  module Database.Selda.MakeSelectors
   ( Selectors, GSelectors
   , selectors, tableWithSelectors
   ) where
  import Control.Monad.State.Strict
      ( MonadState(state), State, evalState )
  import Data.Proxy ( Proxy(..) )
  import GHC.Generics ( Generic(Rep), K1, M1 )
  import qualified GHC.Generics as G
  import Database.Selda.Generic (Relational)
  import Database.Selda.Selectors ( Selector, unsafeSelector )
  import Database.Selda.SqlRow ( SqlRow )
  import Database.Selda.SqlType ( SqlType )
  import Database.Selda.Table ( Table, Attr, table )
  import Database.Selda.Types ( type (:*:)(..), TableName )
  
  -- | Generate selector functions for the given table.
  --   Selectors can be used to access the fields of a query result tuple, avoiding
  --   the need to pattern match on the entire tuple.
  --
  -- > tbl :: Table (Int, Text)
  -- > tbl = table "foo" []
  -- > (tblBar :*: tblBaz) = selectors tbl
  -- >
  -- > q :: Query s Text
  -- > q = do
  -- >   row <- select tbl
  -- >   return (row ! tblBaz)
  selectors :: forall a. (Relational a, GSelectors a (Rep a))
            => Table a
            -> Selectors a
  selectors _ = selectorsFor (Proxy :: Proxy a)
  
  -- | A pair of the table with the given name and columns, and all its selectors.
  --   For example:
  --
  -- > tbl :: Table (Int, Text)
  -- > (tbl, tblBar :*: tblBaz)
  -- >   =  tableWithSelectors "foo" []
  -- >
  -- > q :: Query s Text
  -- > q = tblBaz `from` select tbl
  tableWithSelectors :: forall a. (Relational a, GSelectors a (Rep a))
                     => TableName
                     -> [Attr a]
                     -> (Table a, Selectors a)
  tableWithSelectors name cs = (t, s)
    where
      t = table name cs
      s = selectors t
  
  -- | Generate selectors for the given type.
  selectorsFor :: forall r. GSelectors r (Rep r) => Proxy r -> Selectors r
  selectorsFor = flip evalState 0 . mkSel (Proxy :: Proxy (Rep r))
  
  -- | An inductive tuple of selectors for the given relation.
  type Selectors r = Sels r (Rep r)
  
  type family Sels t f where
    Sels t ((a G.:*: b) G.:*: c) = Sels t (a G.:*: (b G.:*: c))
    Sels t (a G.:*: b)           = Sels t a :*: Sels t b
    Sels t (M1 x y f)            = Sels t f
    Sels t (K1 i a)              = Selector t a
  
  -- | Any table type that can have selectors generated.
  class GSelectors t (f :: * -> *) where
    mkSel :: Proxy f -> Proxy t -> State Int (Sels t f)
  
  instance (SqlRow t, SqlType a) => GSelectors t (K1 i a) where
    mkSel _ _ = unsafeSelector <$> state (\n -> (n, n+1))
  
  instance (GSelectors t f, Sels t f ~ Sels t (M1 x y f)) =>
           GSelectors t (M1 x y f) where
    mkSel _ = mkSel (Proxy :: Proxy f)
  
  instance GSelectors t (a G.:*: (b G.:*: c)) =>
           GSelectors t ((a G.:*: b) G.:*: c) where
    mkSel _ = mkSel (Proxy :: Proxy (a G.:*: (b G.:*: c)))
  
  instance {-# OVERLAPPABLE #-}
    ( GSelectors t a
    , GSelectors t b
    , Sels t (a G.:*: b) ~ (Sels t a :*: Sels t b)
    ) => GSelectors t (a G.:*: b) where
      mkSel _ p = do
        x <- mkSel (Proxy :: Proxy a) p
        xs <- mkSel (Proxy :: Proxy b) p
        return (x :*: xs)
  