{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language LambdaCase #-}
{-# language MultiParamTypeClasses #-}
{-# language StandaloneKindSignatures #-}
{-# language TypeFamilies #-}

module Rel8.Column
  ( Column, AColumn(..)
  , TColumn
  )
where

-- base
import Data.Kind ( Type )
import Prelude

-- rel8
import Rel8.Aggregate ( Aggregate, Col( A ) )
import Rel8.Expr ( Expr, Col( E ) )
import Rel8.FCF ( Eval, Exp )
import Rel8.Kind.Context ( SContext(..), Reifiable( contextSing ) )
import Rel8.Schema.HTable.Identity ( HIdentity( HIdentity ) )
import qualified Rel8.Schema.Kind as K
import Rel8.Schema.Name ( Name(..), Col( N ) )
import Rel8.Schema.Null ( Sql )
import Rel8.Schema.Reify ( Reify, Col(..) )
import Rel8.Schema.Result ( Col( R ), Result )
import Rel8.Schema.Spec ( Spec( Spec ) )
import Rel8.Table
  ( Table, Columns, Context, fromColumns, toColumns
  , Unreify, reify, unreify
  )
import Rel8.Table.Recontextualize ( Recontextualize )
import Rel8.Type ( DBType )


-- | This type family is used to specify columns in 'Rel8able's. In @Column f
-- a@, @f@ is the context of the column (which should be left polymorphic in
-- 'Rel8able' definitions), and @a@ is the type of the column.
type Column :: K.Context -> Type -> Type
type family Column context a where
  Column (Reify context) a = AColumn context a
  Column Aggregate       a = Aggregate a
  Column Expr            a = Expr a
  Column Name            a = Name a
  Column Result          a = a


type AColumn :: K.Context -> Type -> Type
newtype AColumn context a = AColumn (Column context a)


instance (Reifiable context, Sql DBType a) =>
  Table (Reify context) (AColumn context a)
 where
  type Context (AColumn context a) = Reify context
  type Columns (AColumn context a) = HIdentity ('Spec '[] a)
  type Unreify (AColumn context a) = Column context a

  fromColumns :: Columns (AColumn context a) (Col (Reify context))
-> AColumn context a
fromColumns (HIdentity (Reify a)) = SContext context -> Col context ('Spec '[] a) -> AColumn context a
forall (context :: Context) (labels :: Labels) a.
SContext context
-> Col context ('Spec labels a) -> AColumn context a
sfromColumn SContext context
forall (context :: Context). Reifiable context => SContext context
contextSing Col context ('Spec '[] a)
a
  toColumns :: AColumn context a
-> Columns (AColumn context a) (Col (Reify context))
toColumns = Col (Reify context) ('Spec '[] a)
-> HIdentity ('Spec '[] a) (Col (Reify context))
forall (spec :: Spec) (context :: HContext).
context spec -> HIdentity spec context
HIdentity (Col (Reify context) ('Spec '[] a)
 -> HIdentity ('Spec '[] a) (Col (Reify context)))
-> (AColumn context a -> Col (Reify context) ('Spec '[] a))
-> AColumn context a
-> HIdentity ('Spec '[] a) (Col (Reify context))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Col context ('Spec '[] a) -> Col (Reify context) ('Spec '[] a)
forall (context :: Context) (spec :: Spec).
Col context spec -> Col (Reify context) spec
Reify (Col context ('Spec '[] a) -> Col (Reify context) ('Spec '[] a))
-> (AColumn context a -> Col context ('Spec '[] a))
-> AColumn context a
-> Col (Reify context) ('Spec '[] a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SContext context -> AColumn context a -> Col context ('Spec '[] a)
forall (context :: Context) a (labels :: Labels).
SContext context
-> AColumn context a -> Col context ('Spec labels a)
stoColumn SContext context
forall (context :: Context). Reifiable context => SContext context
contextSing
  reify :: (Reify context :~: Reify ctx)
-> Unreify (AColumn context a) -> AColumn context a
reify Reify context :~: Reify ctx
_ = Unreify (AColumn context a) -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn
  unreify :: (Reify context :~: Reify ctx)
-> AColumn context a -> Unreify (AColumn context a)
unreify Reify context :~: Reify ctx
_ (AColumn Column context a
a) = Unreify (AColumn context a)
Column context a
a


instance
  ( Reifiable context, Reifiable context'
  , Sql DBType a
  ) =>
  Recontextualize
    (Reify context)
    (Reify context')
    (AColumn context a)
    (AColumn context' a)


sfromColumn :: ()
  => SContext context
  -> Col context ('Spec labels a)
  -> AColumn context a
sfromColumn :: SContext context
-> Col context ('Spec labels a) -> AColumn context a
sfromColumn = \case
  SContext context
SAggregate -> \(A a) -> Column context a -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn Aggregate a
Column context a
a
  SContext context
SExpr -> \(E a) -> Column context a -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn Expr a
Column context a
a
  SContext context
SName -> \(N a) -> Column context a -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn Name a
Column context a
a
  SContext context
SResult -> \(R a) -> Column context a -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn a
Column context a
a
  SReify SContext context
context -> \(Reify a) -> Column context a -> AColumn context a
forall (context :: Context) a.
Column context a -> AColumn context a
AColumn (SContext context
-> Col context ('Spec labels a) -> AColumn context a
forall (context :: Context) (labels :: Labels) a.
SContext context
-> Col context ('Spec labels a) -> AColumn context a
sfromColumn SContext context
context Col context ('Spec labels a)
a)


stoColumn :: ()
  => SContext context
  -> AColumn context a
  -> Col context ('Spec labels a)
stoColumn :: SContext context
-> AColumn context a -> Col context ('Spec labels a)
stoColumn = \case
  SContext context
SAggregate -> \(AColumn Column context a
a) -> Aggregate a -> Col Aggregate ('Spec labels a)
forall a (labels :: Labels).
Aggregate a -> Col Aggregate ('Spec labels a)
A Aggregate a
Column context a
a
  SContext context
SExpr -> \(AColumn Column context a
a) -> Expr a -> Col Expr ('Spec labels a)
forall a (labels :: Labels). Expr a -> Col Expr ('Spec labels a)
E Expr a
Column context a
a
  SContext context
SName -> \(AColumn Column context a
a) -> Name a -> Col Name ('Spec labels a)
forall a (labels :: Labels). Name a -> Col Name ('Spec labels a)
N Name a
Column context a
a
  SContext context
SResult -> \(AColumn Column context a
a) -> a -> Col Result ('Spec labels a)
forall a (labels :: Labels). a -> Col Result ('Spec labels a)
R a
Column context a
a
  SReify SContext context
context -> \(AColumn Column context a
a) -> Col context ('Spec labels a)
-> Col (Reify context) ('Spec labels a)
forall (context :: Context) (spec :: Spec).
Col context spec -> Col (Reify context) spec
Reify (SContext context
-> AColumn context a -> Col context ('Spec labels a)
forall (context :: Context) a (labels :: Labels).
SContext context
-> AColumn context a -> Col context ('Spec labels a)
stoColumn SContext context
context AColumn context a
Column context a
a)


data TColumn :: K.Context -> Type -> Exp Type
type instance Eval (TColumn f a) = Column f a