{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Data.IsNull.Internal where

import qualified Data.Foldable.Compat as F
import qualified Data.Text            as T
import qualified Data.Text.Lazy       as LT
import qualified Data.ByteString      as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.IntSet          as IS
import Control.Monad (liftM)


class IsNull a where
  -- | isNull ~ isEmpty ~ isInvalid?
  --
  -- >>> isNull (Left 5)
  -- True
  --
  -- >>> isNull ("abc" :: T.Text)
  -- False
  --
  -- >>> isNull [""] -- see isNullN
  -- False
  isNull:: a -> Bool

  -- | Typing causes arthritis. Alias for @'isNull'@.
  isN :: a -> Bool
  isN = isNull

  -- | the logical negation of @'isNull'@
  notNull :: a -> Bool
  notNull = not . isNull

  -- | Nested isNull
  --
  -- >>> isNullN (Just "abc")
  -- False
  --
  -- >>> isNullN (Just "")
  -- True
  --
  -- >>> isNullN (Nothing :: Maybe String)
  -- True
  isNullN :: F.Foldable f => f a -> Bool
  isNullN = F.all isNull

  -- | Nested isNotNull
  notNullN :: F.Foldable f => f a -> Bool
  notNullN = not . isNullN

  -- | Monadic isNull
  --
  -- >>> isNullM [""]
  -- [True]
  isNullM :: Monad m => m a -> m Bool
  isNullM = liftM isNull

  -- | Monadic Nested isNull
  isNullNM :: (Monad m, F.Foldable f) => m (f a) -> m Bool
  isNullNM = liftM isNullN

  -- | @'Alternative'@'s @'<|>'@ operator does not always operate as choice,
  -- at least not in an intuitive way (for example with lists).
  -- This one does:
  --
  -- >>> [2,3] <\> [4,5]
  -- [2,3]
  --
  -- >>> [] <\> [4,5]
  -- [4,5]
  --
  -- >>> [] <\> []
  -- []
  (<\>) :: a -> a -> a
  (<\>) a b = if isNull a then b else a
  infixl 3 <\>

instance IsNull Bool where
  isNull = not

instance IsNull T.Text where
  isNull = T.null

instance IsNull LT.Text where
  isNull = LT.null

instance IsNull BS.ByteString where
  isNull = BS.null

instance IsNull LBS.ByteString where
  isNull = LBS.null

instance IsNull IS.IntSet where
  isNull = IS.null

instance F.Foldable f => IsNull (f a) where
  isNull = F.foldr (\_ _ -> False) True