    {-# LANGUAGE CPP #-}
    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE Trustworthy #-}
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE TemplateHaskell #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE DeriveDataTypeable #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE DefaultSignatures #-}
    {-# OPTIONS_GHC -fno-warn-orphans #-}
    --------------------------------------------------------------------
    -- |
    -- Copyright :  (c) Edward Kmett 2013, (c) Paul Wilson 2012
    -- License   :  BSD3
    -- Maintainer:  Edward Kmett <ekmett@gmail.com>
    -- Stability :  experimental
    -- Portability: non-portable
    --
    --------------------------------------------------------------------
    module Control.Lens.Aeson
      (
      -- * Numbers
        AsNumber(..)
      , integralValue
      , nonNull
      -- * Primitive
      , Primitive(..)
      , AsPrimitive(..)
      -- * Objects and Arrays
      , AsValue(..)
      , key, members
      , nth, values
      -- * Decoding
      , AsJSON(..)
      ) where

    import Control.Applicative
    import Control.Lens
    import Data.Aeson
    import Data.Attoparsec.Number
    import Data.ByteString.Lazy.Char8 as Lazy hiding (putStrLn)
    import Data.ByteString.Lazy.UTF8 as UTF8 hiding (decode)
    import Data.Data
    import Data.HashMap.Strict (HashMap)
    import Data.Text
    import Data.Vector (Vector)
    import Numeric.Lens
    import Prelude hiding(null)

    -- $setup
    -- >>> :set -XOverloadedStrings

    ------------------------------------------------------------------------------
    -- Number prisms
    ------------------------------------------------------------------------------

    class AsNumber t where
      -- |
      -- >>> "[1, \"x\"]" ^? nth 0 . _Number
      -- Just 1
      --
      -- >>> "[1, \"x\"]" ^? nth 1 . _Number
      -- Nothing
      _Number :: Prism' t Number
    #ifndef HLINT
      default _Number :: AsPrimitive t => Prism' t Number
      _Number = _Primitive._Number
    #endif

      -- |
      -- Prism into an 'Double' over a 'Value', 'Primitive' or 'Number'
      --
      -- >>> "[10.2]" ^? nth 0 . _Double
      -- Just 10.2
      _Double :: Prism' t Double
      _Double = _Number.prism D (\v -> case v of D d -> Right d; _ -> Left v)

      -- |
      -- Prism into an 'Integer' over a 'Value', 'Primitive' or 'Number'
      --
      -- >>> "[10]" ^? nth 0 . _Integer
      -- Just 10
      --
      -- >>> "[10.5]" ^? nth 0 . _Integer
      -- Nothing
      _Integer :: Prism' t Integer
      _Integer = _Number.prism I (\v -> case v of I i -> Right i; _ -> Left v)

    instance AsNumber Value where
      _Number = prism Number $ \v -> case v of Number n -> Right n; _ -> Left v

    instance AsNumber Number where
      _Number = id

    instance AsNumber ByteString
    instance AsNumber String

    ------------------------------------------------------------------------------
    -- Conversion Prisms
    ------------------------------------------------------------------------------

    -- | Access Integer 'Value's as Integrals.
    --
    -- defined as `integer . 'Numeric.Lens.integral'`
    --
    -- >>> "[10]" ^? nth 0 . integralValue
    -- Just 10
    --
    -- >>> "[10.5]" ^? nth 0 . integralValue
    -- Nothing
    integralValue :: (AsNumber t, Integral a) => Prism' t a
    integralValue = _Integer . integral

    ------------------------------------------------------------------------------
    -- Null values and primitives
    ------------------------------------------------------------------------------

    -- | Primitives of 'Value'
    data Primitive
      = StringPrim !Text
      | NumberPrim !Number
      | BoolPrim !Bool
      | NullPrim
      deriving (Eq,Ord,Show,Data,Typeable)

    instance AsNumber Primitive where
      _Number = prism NumberPrim $ \v -> case v of NumberPrim s -> Right s; _ -> Left v

    class AsNumber t => AsPrimitive t where
      -- |
      -- >>> "[1, \"x\", null, true, false]" ^? nth 0 . _Primitive
      -- Just (NumberPrim 1)
      --
      -- >>> "[1, \"x\", null, true, false]" ^? nth 1 . _Primitive
      -- Just (StringPrim "x")
      --
      -- >>> "[1, \"x\", null, true, false]" ^? nth 2 . _Primitive
      -- Just NullPrim
      --
      -- >>> "[1, \"x\", null, true, false]" ^? nth 3 . _Primitive
      -- Just (BoolPrim True)
      --
      -- >>> "[1, \"x\", null, true, false]" ^? nth 4 . _Primitive
      -- Just (BoolPrim False)
      _Primitive :: Prism' t Primitive
    #ifndef HLINT
      default _Primitive :: AsValue t => Prism' t Primitive
      _Primitive = _Value._Primitive
    #endif

      -- "{\"a\": \"xyz\", \"b\": true}" ^? key "a" . _String
      -- Just "xyz"
      --
      -- >>> "{\"a\": \"xyz\", \"b\": true}" ^? key "b" . _String
      -- Nothing
      _String :: Prism' t Text
      _String = _Primitive.prism StringPrim (\v -> case v of StringPrim s -> Right s; _ -> Left v)

      -- >>> "{\"a\": \"xyz\", \"b\": true}" ^? key "b" . _Bool
      -- Just True
      --
      -- "{\"a\": \"xyz\", \"b\": true}" ^? key "a" . _Bool
      -- Nothing
      _Bool :: Prism' t Bool
      _Bool = _Primitive.prism BoolPrim (\v -> case v of BoolPrim b -> Right b; _ -> Left v)

      -- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "b" . _Null
      -- Just ()
      --
      -- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "a" . _Null
      -- Nothing
      _Null :: Prism' t ()
      _Null = _Primitive.prism (const NullPrim) (\v -> case v of NullPrim -> Right (); _ -> Left v)

    instance AsPrimitive Value where
      _Primitive = prism fromPrim toPrim
        where
          toPrim (String s) = Right $ StringPrim s
          toPrim (Number n) = Right $ NumberPrim n
          toPrim (Bool b)   = Right $ BoolPrim b
          toPrim Null       = Right $ NullPrim
          toPrim v          = Left v
          fromPrim (StringPrim s) = String s
          fromPrim (NumberPrim n) = Number n
          fromPrim (BoolPrim b)   = Bool b
          fromPrim NullPrim       = Null

      _String = prism String $ \v -> case v of String s -> Right s; _ -> Left v
      _Bool = prism Bool (\v -> case v of Bool b -> Right b; _ -> Left v)
      _Null = prism (const Null) (\v -> case v of Null -> Right (); _ -> Left v)

    instance AsPrimitive ByteString
    instance AsPrimitive String

    instance AsPrimitive Primitive where
      _Primitive = id

    -- | Prism into non-'Null' values
    --
    -- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "a" . nonNull
    -- Just (String "xyz")
    --
    -- >>> "{\"a\": {}, \"b\": null}" ^? key "a" . nonNull
    -- Just (Object fromList [])
    --
    -- >>> "{\"a\": \"xyz\", \"b\": null}" ^? key "b" . nonNull
    -- Nothing
    nonNull :: Prism' Value Value
    nonNull = prism id (\v -> if isn't _Null v then Right v else Left v)

    ------------------------------------------------------------------------------
    -- Non-primitive traversals
    ------------------------------------------------------------------------------

    class AsPrimitive t => AsValue t where
      -- |
      -- >>>"[1,2,3]" ^? _Value
      -- Just (Array (fromList [Number 1,Number 2,Number 3]))
      _Value :: Prism' t Value

      -- |
      -- >>> "{\"a\": {}, \"b\": null}" ^? key "a" . _Object
      -- Just fromList []
      --
      -- >>> "{\"a\": {}, \"b\": null}" ^? key "b" . _Object
      -- Nothing
      _Object :: Prism' t (HashMap Text Value)
      _Object = _Value.prism Object (\v -> case v of Object o -> Right o; _ -> Left v)

      -- |
      -- >>> "[1,2,3]" ^? _Array
      -- Just (fromList [Number 1,Number 2,Number 3])
      _Array :: Prism' t (Vector Value)
      _Array = _Value.prism Array (\v -> case v of Array a -> Right a; _ -> Left v)

    instance AsValue Value where
      _Value = id

    instance AsValue ByteString where
      _Value = _JSON

    instance AsValue String where
      _Value = iso UTF8.fromString UTF8.toString._Value

    -- |
    -- Like 'ix', but for 'Object' with Text indices. This often has better
    -- inference than 'ix' when used with OverloadedStrings.
    --
    -- >>> "{\"a\": 100, \"b\": 200}" ^? key "a"
    -- Just (Number 100)
    --
    -- >>> "[1,2,3]" ^? key "a"
    -- Nothing
    key :: AsValue t => Text -> Traversal' t Value
    key i = _Object . ix i

    members :: AsValue t => IndexedTraversal' Text t Value
    members = _Object . each

    -- | Like 'ix', but for Arrays with Int indexes
    --
    -- >>> "[1,2,3]" ^? nth 1
    -- Just (Number 2)
    --
    -- >>> "\"a\": 100, \"b\": 200}" ^? nth 1
    -- Nothing
    --
    -- >>> "[1,2,3]" & nth 1 .~ (Number 20)
    -- "[1,20,3]"
    nth :: AsValue t => Int -> Traversal' t Value
    nth i = _Array . ix i

    values :: AsValue t => IndexedTraversal' Int t Value
    values = _Array . traversed

    class AsJSON t where
      -- | A Prism into 'Value' on lazy 'ByteString's.
      _JSON :: (FromJSON a, ToJSON a) => Prism' t a

    instance AsJSON Lazy.ByteString where
      _JSON = prism' encode decode

    instance AsJSON String where
      _JSON = iso UTF8.fromString UTF8.toString._JSON

    ------------------------------------------------------------------------------
    -- Orphan instances
    ------------------------------------------------------------------------------

    type instance Index Value = Text
    type instance IxValue Value = Value

    instance Applicative f => Ixed f Value where
      ix i = _Object.ix i

    instance (Applicative f, Gettable f) => Contains f Value where
      contains i f (Object o) = coerce (contains i f o)
      contains i f _ = coerce (indexed f i False)

    instance Plated Value where
      plate f (Object o) = Object <$> traverse f o
      plate f (Array a) = Array <$> traverse f a
      plate _ xs = pure xs