{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}

module DSV.ZipViewPipe
  ( zipViewPipe
  , zipViewPipeIgnoringAllErrors
  , zipViewPipeThrowFirstError
  ) where

import DSV.ByteString
import DSV.IO
import DSV.Pipes
import DSV.Position
import DSV.Prelude
import DSV.Validation
import DSV.Vector
import DSV.ViewType
import DSV.ZipViewType

-- pipes
import qualified Pipes.Prelude as P

zipViewPipe ::
    forall m headerError rowError row .
    Monad m
    => ZipView headerError rowError row
      -- ^ A specification of how to interpret the header and rows
    -> Pipe (Vector ByteString) (Validation rowError row) m headerError
      -- ^ The first vector that this pipe 'await's is the header. If the header is invalid, the pipe closes and 'return's the @headerError@. Otherwise, the pipe continues indefinitely; for each subsequent @'Vector' 'ByteString'@, it 'yield's one @'Validation' rowError row@.

zipViewPipe (ZipView (View f)) =
  do
    header <- await
    case (f header) of
        Failure err -> return err
        Success (View g) -> P.map g

zipViewPipeIgnoringAllErrors ::
    forall m headerError rowError row .
    Monad m
    => ZipView headerError rowError row
      -- ^ A specification of how to interpret the header and rows
    -> Pipe (Vector ByteString) row m ()
      -- ^ The first vector that this pipe 'await's is the header. If the header is invalid, the pipe closes and 'return's @()@. Otherwise, the pipe continues indefinitely; for each subsequent @'Vector' 'ByteString'@, it 'yield's a @row@ if the row is valid, or otherwise does nothing if the row is malformed.

zipViewPipeIgnoringAllErrors (ZipView (View f)) =
  do
    header <- await
    case (f header) of
        Failure _ -> return ()
        Success (View g) -> P.mapFoldable g

zipViewPipeThrowFirstError ::
    forall m headerError rowError row r .
    ( Monad m, MonadThrow m
    , Exception headerError
    , Show rowError, Typeable rowError
    )
    => ZipView headerError rowError row
      -- ^ A specification of how to interpret the header and rows
    -> Pipe (Vector ByteString) row m r
      -- ^ The first vector that this pipe 'await's is the header. If the header is invalid, the pipe throws the @headerError@ as an exception in @m@. For each subsequent @'Vector' 'ByteString'@, the pipe 'yield's a @row@ if the row is valid, or otherwise throws the @rowError@ as an exception in @m@.

zipViewPipeThrowFirstError (ZipView (View f)) =
  do
    header <- await
    case (f header) of
        Failure e -> throwM e
        Success v -> go v (RowNumber 1)
  where
    go v (RowNumber n) =
      do
        x <- await
        case applyView v x of
            Failure e -> throwM (At (RowNumber n) e)
            Success row ->
              do
                yield row
                go v (RowNumber (n + 1))