content: module Rel8.Expr.Window
    ( cumulative
    , rowNumber
    , rank
    , denseRank
    , percentRank
    , cumeDist
    , ntile
    , lagExpr, lagExprOn
    , leadExpr, leadExprOn
    , firstValueExpr, firstValueExprOn
    , lastValueExpr, lastValueExprOn
    , nthValueExpr, nthValueExprOn
    )
  where
  
  -- base
  import Data.Int ( Int32, Int64 )
  import Prelude
  
  -- opaleye
  import qualified Opaleye.Internal.Aggregate as Opaleye
  import qualified Opaleye.Internal.PackMap as Opaleye
  import qualified Opaleye.Internal.Window as Opaleye
  import qualified Opaleye.Window as Opaleye
  
  -- profunctors
  import Data.Profunctor (dimap, lmap)
  
  -- rel8
  import Rel8.Aggregate (Aggregator' (Aggregator))
  import Rel8.Expr ( Expr )
  import Rel8.Expr.Opaleye ( fromColumn, fromPrimExpr, toColumn, toPrimExpr )
  import Rel8.Schema.Null ( Nullify )
  import Rel8.Window ( Window( Window ) )
  
  
  -- | 'cumulative' allows the use of aggregation functions in 'Window'
  -- expressions. In particular, @'cumulative' 'Rel8.sum'@
  -- (when combined with 'Rel8.Window.orderPartitionBy') gives a running total,
  -- also known as a \"cumulative sum\", hence the name @cumulative@.
  cumulative :: Aggregator' fold i a -> Window i a
  cumulative f =
    fromWindowFunction $ Opaleye.aggregatorWindowFunction (fromAggregate f) id
  
  
  -- | [@row_number()@](https://www.postgresql.org/docs/current/functions-window.html)
  rowNumber :: Window i (Expr Int64)
  rowNumber = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.rowNumber
  
  
  -- | [@rank()@](https://www.postgresql.org/docs/current/functions-window.html)
  rank :: Window i (Expr Int64)
  rank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.rank
  
  
  -- | [@dense_rank()@](https://www.postgresql.org/docs/current/functions-window.html)
  denseRank :: Window i (Expr Int64)
  denseRank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.denseRank
  
  
  -- | [@percent_rank()@](https://www.postgresql.org/docs/current/functions-window.html)
  percentRank :: Window i (Expr Double)
  percentRank = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.percentRank
  
  
  -- | [@cume_dist()@](https://www.postgresql.org/docs/current/functions-window.html)
  cumeDist :: Window i (Expr Double)
  cumeDist = fromWindowFunction $ fromPrimExpr . fromColumn <$> Opaleye.cumeDist
  
  
  -- | [@ntile(num_buckets)@](https://www.postgresql.org/docs/current/functions-window.html)
  ntile :: Expr Int32 -> Window i (Expr Int32)
  ntile buckets = fromWindowFunction $ fromPrimExpr . fromColumn <$>
    Opaleye.ntile (toColumn (toPrimExpr buckets))
  
  
  -- | [@lag(value, offset, default)@](https://www.postgresql.org/docs/current/functions-window.html)
  lagExpr :: Expr Int32 -> Expr a -> Window (Expr a) (Expr a)
  lagExpr offset def =
    fromWindowFunction $
      dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
        Opaleye.lag (toColumn (toPrimExpr offset)) (toColumn (toPrimExpr def))
  
  
  -- | Applies 'lag' to the column selected by the given function.
  lagExprOn :: Expr Int32 -> Expr a -> (i -> Expr a) -> Window i (Expr a)
  lagExprOn offset def f = lmap f (lagExpr offset def)
  
  
  -- | [@lead(value, offset, default)@](https://www.postgresql.org/docs/current/functions-window.html)
  leadExpr :: Expr Int32 -> Expr a -> Window (Expr a) (Expr a)
  leadExpr offset def =
    fromWindowFunction $
      dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
        Opaleye.lead (toColumn (toPrimExpr offset)) (toColumn (toPrimExpr def))
  
  
  -- | Applies 'lead' to the column selected by the given function.
  leadExprOn :: Expr Int32 -> Expr a -> (i -> Expr a) -> Window i (Expr a)
  leadExprOn offset def f = lmap f (leadExpr offset def)
  
  
  -- | [@first_value(value)@](https://www.postgresql.org/docs/current/functions-window.html)
  firstValueExpr :: Window (Expr a) (Expr a)
  firstValueExpr =
    fromWindowFunction $
      dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
        Opaleye.firstValue
  
  
  -- | Applies 'firstValue' to the column selected by the given function.
  firstValueExprOn :: (i -> Expr a) -> Window i (Expr a)
  firstValueExprOn f = lmap f firstValueExpr
  
  
  -- | [@last_value(value)@](https://www.postgresql.org/docs/current/functions-window.html)
  lastValueExpr :: Window (Expr a) (Expr a)
  lastValueExpr =
    fromWindowFunction $
      dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
        Opaleye.lastValue
  
  
  -- | Applies 'lastValue' to the column selected by the given function.
  lastValueExprOn :: (i -> Expr a) -> Window i (Expr a)
  lastValueExprOn f = lmap f lastValueExpr
  
  
  -- | [@nth_value(value, n)@](https://www.postgresql.org/docs/current/functions-window.html)
  nthValueExpr :: Expr Int32 -> Window (Expr a) (Expr (Nullify a))
  nthValueExpr n =
    fromWindowFunction $
      dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
        Opaleye.nthValue (toColumn (toPrimExpr n))
  
  
  -- | [@nth_value(value, n)@](https://www.postgresql.org/docs/current/functions-window.html)
  nthValueExprOn :: Expr Int32 -> (i -> Expr a) -> Window i (Expr (Nullify a))
  nthValueExprOn n f = lmap f (nthValueExpr n)
  
  
  fromAggregate :: Aggregator' fold i a -> Opaleye.Aggregator i a
  fromAggregate (Aggregator _ a) = a
  
  
  fromWindowFunction :: Opaleye.WindowFunction i a -> Window i a
  fromWindowFunction (Opaleye.WindowFunction (Opaleye.PackMap w)) =
    Window $ Opaleye.Windows $ Opaleye.PackMap $ \f -> w $ \o -> f (o, mempty)
  