{-# LANGUAGE DeriveFoldable        #-}
{-# LANGUAGE DeriveFunctor         #-}
{-# LANGUAGE DeriveTraversable     #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE TypeFamilies          #-}
module Waargonaut.Types.JArray
  (
    
    JArray (..)
    
  , parseJArray
  , jArrayBuilder
  ) where
import           Prelude                   (Eq, Show)
import           Control.Category          ((.))
import           Control.Error.Util        (note)
import           Control.Lens              (AsEmpty (..), Cons (..), Rewrapped,
                                            Wrapped (..), cons, isn't, iso,
                                            nearly, over, prism, to, ( # ),
                                            (^.), (^?), _2, _Wrapped)
import           Control.Monad             (Monad)
import           Data.Bifoldable           (Bifoldable (bifoldMap))
import           Data.Bifunctor            (Bifunctor (bimap))
import           Data.Bitraversable        (Bitraversable (bitraverse))
import           Data.Foldable             (Foldable)
import           Data.Function             (($))
import           Data.Functor              (Functor, (<$>))
import           Data.Monoid               (Monoid (..), mempty)
import           Data.Semigroup            (Semigroup (..))
import           Data.Traversable          (Traversable)
import           Data.ByteString.Builder   (Builder)
import           Text.Parser.Char          (CharParsing, char)
import           Waargonaut.Types.CommaSep (CommaSeparated,
                                            commaSeparatedBuilder,
                                            parseCommaSeparated)
newtype JArray ws a =
  JArray (CommaSeparated ws a)
  deriving (Eq, Show, Functor, Foldable, Traversable)
instance JArray ws a ~ t => Rewrapped (JArray ws a) t
instance Wrapped (JArray ws a) where
  type Unwrapped (JArray ws a) = CommaSeparated ws a
  _Wrapped' = iso (\(JArray x) -> x) JArray
instance Monoid ws => Cons (JArray ws a) (JArray ws a) a a where
  _Cons = prism
    (\(a,j) -> over _Wrapped (cons a) j)
    (\j -> note j $ over _2 (_Wrapped #) <$> j ^? _Wrapped . _Cons)
  {-# INLINE _Cons #-}
instance (Semigroup ws, Monoid ws) => AsEmpty (JArray ws a) where
  _Empty = nearly (JArray mempty) (^. _Wrapped . to (isn't _Empty))
  {-# INLINE _Empty #-}
instance (Monoid ws, Semigroup ws) => Semigroup (JArray ws a) where
  (JArray a) <> (JArray b) = JArray (a <> b)
instance (Semigroup ws, Monoid ws) => Monoid (JArray ws a) where
  mempty = JArray mempty
  mappend = (<>)
instance Bifunctor JArray where
  bimap f g (JArray cs) = JArray (bimap f g cs)
instance Bifoldable JArray where
  bifoldMap f g (JArray cs) = bifoldMap f g cs
instance Bitraversable JArray where
  bitraverse f g (JArray cs) = JArray <$> bitraverse f g cs
parseJArray
  :: ( Monad f
     , CharParsing f
     )
  => f ws
  -> f a
  -> f (JArray ws a)
parseJArray ws a = JArray <$>
  parseCommaSeparated (char '[') (char ']') ws a
jArrayBuilder
  :: (ws -> Builder)
  -> ((ws -> Builder) -> a -> Builder)
  -> JArray ws a
  -> Builder
jArrayBuilder ws a (JArray cs) =
  commaSeparatedBuilder '[' ']' ws (a ws) cs